102 changes: 101 additions & 1 deletion src/app/qgsattributetypedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "qgisapp.h"
#include "qgsproject.h"
#include "qgslogger.h"
#include "qgseditorwidgetfactory.h"
#include "qgseditorwidgetregistry.h"

#include <QTableWidgetItem>
#include <QFile>
Expand All @@ -47,6 +49,16 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl )
connect( tableWidget, SIGNAL( cellChanged( int, int ) ), this, SLOT( vCellChanged( int, int ) ) );
connect( valueRelationEditExpression, SIGNAL( clicked() ), this, SLOT( editValueRelationExpression() ) );

QMapIterator<QString, QgsEditorWidgetFactory*> i( QgsEditorWidgetRegistry::instance()->factories() );
while ( i.hasNext() )
{
i.next();
QListWidgetItem* item = new QListWidgetItem( selectionListWidget );
item->setText( i.value()->name() );
item->setData( Qt::UserRole, i.key() );
selectionListWidget->addItem( item );
}

valueRelationLayer->clear();
foreach ( QgsMapLayer *l, QgsMapLayerRegistry::instance()->mapLayers() )
{
Expand All @@ -69,6 +81,53 @@ QgsVectorLayer::EditType QgsAttributeTypeDialog::editType()
return mEditType;
}

const QString QgsAttributeTypeDialog::editorWidgetV2Type()
{
QListWidgetItem* item = selectionListWidget->currentItem();
if ( item )
{
return item->data( Qt::UserRole ).toString();
}
else
{
return QString();
}
}

const QString QgsAttributeTypeDialog::editorWidgetV2Text()
{
QListWidgetItem* item = selectionListWidget->currentItem();
if ( item )
{
return item->text();
}
else
{
return QString();
}
}

const QMap<QString, QVariant> QgsAttributeTypeDialog::editorWidgetV2Config()
{
QListWidgetItem* item = selectionListWidget->currentItem();
if ( item )
{
QString widgetType = item->data( Qt::UserRole ).toString();
QgsEditorConfigWidget* cfgWdg = mEditorConfigWidgets[ widgetType ];
if ( cfgWdg )
{
return cfgWdg->config();
}
}

return QMap<QString, QVariant>();
}

void QgsAttributeTypeDialog::setWidgetV2Config( const QMap<QString, QVariant>& config )
{
mWidgetV2Config = config;
}

QgsVectorLayer::RangeData QgsAttributeTypeDialog::rangeData()
{
return mRangeData;
Expand Down Expand Up @@ -357,6 +416,10 @@ void QgsAttributeTypeDialog::setPageForEditType( QgsVectorLayer::EditType editTy
case QgsVectorLayer::Color:
setPage( 16 );
break;

case QgsVectorLayer::EditorWidgetV2:
setPage( 17 );
break;
}
}

Expand Down Expand Up @@ -544,6 +607,7 @@ void QgsAttributeTypeDialog::setIndex( int index, QgsVectorLayer::EditType editT
case QgsVectorLayer::TextEdit:
case QgsVectorLayer::UuidGenerator:
case QgsVectorLayer::Color:
case QgsVectorLayer::EditorWidgetV2:
break;
}
}
Expand Down Expand Up @@ -612,7 +676,40 @@ void QgsAttributeTypeDialog::setStackPage( int index )
stackedWidget->setCurrentIndex( 15 );
break;
default:
stackedWidget->setCurrentIndex( index );
if ( selectionListWidget->item( index )->data( Qt::UserRole ).isNull() )
{
stackedWidget->setCurrentIndex( index );
}
else
{
QString factoryId = selectionListWidget->item( index )->data( Qt::UserRole ).toString();

// Set to (empty) editor widget page
stackedWidget->setCurrentIndex( 16 );

if ( mEditorConfigWidgets.contains( factoryId ) )
{
mEditorConfigWidgets[factoryId]->show();
}
else
{
QgsEditorConfigWidget* cfgWdg = QgsEditorWidgetRegistry::instance()->createConfigWidget( factoryId, mLayer, mIndex, this );
QgsEditorConfigWidget* oldWdg = pageEditorWidget->findChild<QgsEditorConfigWidget*>();

if ( oldWdg )
{
oldWdg->hide();
}

if ( cfgWdg )
{
cfgWdg->setConfig( mWidgetV2Config );
pageEditorWidget->layout()->addWidget( cfgWdg );

mEditorConfigWidgets.insert( factoryId, cfgWdg );
}
}
}
break;
}

Expand Down Expand Up @@ -740,6 +837,9 @@ void QgsAttributeTypeDialog::accept()
case 16:
mEditType = QgsVectorLayer::Color;
break;
case 17:
mEditType = QgsVectorLayer::EditorWidgetV2;
break;
}

QDialog::accept();
Expand Down
13 changes: 13 additions & 0 deletions src/app/qgsattributetypedialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "ui_qgsattributetypeedit.h"

#include "qgsvectorlayer.h"
#include "qgseditorconfigwidget.h"

class QDialog;
class QLayout;
Expand Down Expand Up @@ -58,6 +59,14 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
QgsVectorLayer::EditType editType();

const QString editorWidgetV2Type();

const QString editorWidgetV2Text();

const QMap<QString, QVariant> editorWidgetV2Config();

void setWidgetV2Config( const QMap<QString, QVariant>& config );

/**
* Setter to value map variable to display actual value
* @param valueMap map which is to be dispayed in this dialog
Expand Down Expand Up @@ -224,6 +233,10 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
QgsVectorLayer::EditType mEditType;
QString mDateFormat;
QSize mWidgetSize;

QMap<QString, QVariant> mWidgetV2Config;

QMap< QString, QgsEditorConfigWidget* > mEditorConfigWidgets;
};

#endif
676 changes: 412 additions & 264 deletions src/app/qgsfieldsproperties.cpp

Large diffs are not rendered by default.

112 changes: 93 additions & 19 deletions src/app/qgsfieldsproperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,101 @@
#include <QPushButton>
#include <QTreeWidget>
#include <QTableWidget>
#include <QMimeData>

#include "qgsvectorlayer.h"
#include "ui_qgsfieldspropertiesbase.h"

class APP_EXPORT QgsAttributesTree : public QTreeWidget
class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPropertiesBase
{
Q_OBJECT

public:
QgsAttributesTree( QWidget* parent = 0 )
: QTreeWidget( parent )
{}
QTreeWidgetItem* addContainer( QTreeWidgetItem* parent , QString title );
QTreeWidgetItem* addItem( QTreeWidgetItem* parent , QString fieldName );

protected:
virtual void dragMoveEvent( QDragMoveEvent *event );
virtual void dropEvent( QDropEvent *event );
virtual bool dropMimeData( QTreeWidgetItem * parent, int index, const QMimeData * data, Qt::DropAction action );
/* Qt::DropActions supportedDropActions() const;*/
};
enum FieldPropertiesRoles
{
DesignerTreeRole = Qt::UserRole,
FieldConfigRole
};

class DesignerTreeItemData
{
public:
enum Type
{
Field,
Container
};

class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPropertiesBase
{
Q_OBJECT
DesignerTreeItemData()
{}

public:
DesignerTreeItemData( Type type, const QString& name )
: mType( type )
, mName( name ) {}

QString name() const { return mName; }
void setName( const QString& name ) { mName = name; }

Type type() const { return mType; }
void setType( const Type& type ) { mType = type; }

QVariant asQVariant() { return QVariant::fromValue<DesignerTreeItemData>( *this ); }

protected:
Type mType;
QString mName;
};

/**
* This class overrides mime type handling to be able to work with
* the drag and drop attribute editor.
*
* The mime type is application/x-qgsattributetablefield
*/

class DragList : public QTableWidget
{
public:
DragList( QWidget* parent = 0 )
: QTableWidget( parent )
{}

// QTreeWidget interface
protected:
virtual QStringList mimeTypes() const;

virtual QMimeData* mimeData( const QList<QTableWidgetItem*> items ) const;
};


/**
* Graphical representation for the attribute editor drag and drop editor
*/
class DesignerTree : public QTreeWidget
{
public:
DesignerTree( QWidget* parent = 0 )
: QTreeWidget( parent )
{}
QTreeWidgetItem* addItem( QTreeWidgetItem* parent, DesignerTreeItemData data );
QTreeWidgetItem* addContainer( QTreeWidgetItem* parent, QString title );

protected:
virtual void dragMoveEvent( QDragMoveEvent *event );
virtual void dropEvent( QDropEvent *event );
virtual bool dropMimeData( QTreeWidgetItem * parent, int index, const QMimeData * data, Qt::DropAction action );
/* Qt::DropActions supportedDropActions() const;*/

// QTreeWidget interface
protected:
virtual QStringList mimeTypes() const;
virtual QMimeData* mimeData( const QList<QTreeWidgetItem*> items ) const;
};

/**
* Holds the configuration for a field
*/
class FieldConfig
{
public:
Expand All @@ -64,6 +132,8 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
QPushButton* mButton;
QString mDateFormat;
QSize mWidgetSize;
QString mEditorWidgetV2Type;
QMap<QString, QVariant> mEditorWidgetV2Config;
};

public:
Expand Down Expand Up @@ -125,8 +195,8 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
void setConfigForRow( int row, FieldConfig cfg );

QgsVectorLayer* mLayer;
QgsAttributesTree* mAttributesTree;
QTableWidget* mAttributesList;
DesignerTree* mDesignerTree;
DragList* mFieldsList;

// Holds all the first column items (header: id) of the table.
// The index in the list is the fieldIdx, and therefore acts as a mapping
Expand All @@ -152,10 +222,14 @@ class APP_EXPORT QgsFieldsProperties : public QWidget, private Ui_QgsFieldsPrope
static QMap< QgsVectorLayer::EditType, QString > editTypeMap;
static void setupEditTypes();
static QString editTypeButtonText( QgsVectorLayer::EditType type );
static QgsVectorLayer::EditType editTypeFromButtonText( QString text );
static QgsVectorLayer::EditType editTypeFromButton( QPushButton* btn );

};

QDataStream& operator<< ( QDataStream& stream, const QgsFieldsProperties::DesignerTreeItemData& data );
QDataStream& operator>> ( QDataStream& stream, QgsFieldsProperties::DesignerTreeItemData& data );

Q_DECLARE_METATYPE( QgsFieldsProperties::FieldConfig )
Q_DECLARE_METATYPE( QgsFieldsProperties::DesignerTreeItemData )

#endif // QGSFIELDSPROPERTIES_H
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ SET(QGIS_CORE_HDRS
qgsdataitem.h
qgsdistancearea.h
qgscsexception.h
qgseditorwidgetconfig.h
qgserror.h
qgsexception.h
qgsexpression.h
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsdistancearea.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class CORE_EXPORT QgsDistanceArea
void setSourceAuthId( QString authid );

//! returns source spatial reference system
long sourceCrs() { return mSourceRefSys; }
long sourceCrs() const { return mSourceRefSys; }
//! What sort of coordinate system is being used?
bool geographic() { return mCoordTransform->sourceCrs().geographicFlag(); }

Expand Down
19 changes: 19 additions & 0 deletions src/core/qgseditorwidgetconfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <QMap>
#include <QString>
#include <QVariant>


/**
* Holds a set of configuration parameters for a editor widget wrapper.
* It's basically a set of key => value pairs.
*
* If you need more advanced structures than a simple key => value pair,
* you can use a value to hold any structure a QVariant can handle (and that's
* about anything you get through your compiler)
*
* These are the user configurable options in the field properties tab of the
* vector layer properties. They are saved in the project file per layer and field.
* You get these passed, for every new widget wrapper.
*/

typedef QMap<QString, QVariant> QgsEditorWidgetConfig;
11 changes: 3 additions & 8 deletions src/core/qgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1666,15 +1666,10 @@ void QgsExpression::initGeomCalculator()
mCalc->setEllipsoidalMode( false );
}

void QgsExpression::setGeomCalculator( QgsDistanceArea &calc )
void QgsExpression::setGeomCalculator( const QgsDistanceArea &calc )
{
if ( !mCalc )
mCalc = new QgsDistanceArea();

// Copy from supplied calculator
mCalc->setEllipsoid( calc.ellipsoid() );
mCalc->setEllipsoidalMode( calc.ellipsoidalEnabled() );
mCalc->setSourceCrs( calc.sourceCrs() );
delete mCalc;
mCalc = new QgsDistanceArea( calc );
}

bool QgsExpression::prepare( const QgsFields& fields )
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsexpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class CORE_EXPORT QgsExpression

//! Sets the geometry calculator used in evaluation of expressions,
// instead of the default.
void setGeomCalculator( QgsDistanceArea &calc );
void setGeomCalculator( const QgsDistanceArea &calc );

/** This function currently replaces each expression between [% and %]
in the string with the result of its evaluation on the feature
Expand Down
54 changes: 49 additions & 5 deletions src/core/qgsvectorlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1964,6 +1964,7 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
case UniqueValuesEditable:
case UuidGenerator:
case Color:
case EditorWidgetV2: // Will get a signal and read there
break;
}
}
Expand Down Expand Up @@ -2086,6 +2087,7 @@ bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage
QgsAttributeEditorElement *attributeEditorWidget = attributeEditorElementFromDomElement( elem, this );
mAttributeEditorElements.append( attributeEditorWidget );
}

return true;
}

Expand All @@ -2103,7 +2105,8 @@ QgsAttributeEditorElement* QgsVectorLayer::attributeEditorElementFromDomElement(
{
QDomElement childElem = childNodeList.at( i ).toElement();
QgsAttributeEditorElement* myElem = attributeEditorElementFromDomElement( childElem, container );
container->addChildElement( myElem );
if ( myElem )
container->addChildElement( myElem );
}

newElement = container;
Expand All @@ -2114,7 +2117,6 @@ QgsAttributeEditorElement* QgsVectorLayer::attributeEditorElementFromDomElement(
int idx = *( dataProvider()->fieldNameMap() ).find( name );
newElement = new QgsAttributeEditorField( name, idx, parent );
}

return newElement;
}

Expand Down Expand Up @@ -2282,6 +2284,7 @@ bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString&
case Immutable:
case UuidGenerator:
case Color:
case EditorWidgetV2: // Will get a signal and save there
break;
}

Expand Down Expand Up @@ -2479,6 +2482,16 @@ void QgsVectorLayer::addAttributeEditorWidget( QgsAttributeEditorElement* data )
mAttributeEditorElements.append( data );
}

const QString QgsVectorLayer::editorWidgetV2( int fieldIdx )
{
return mEditorWidgetV2Types.value( fieldIdx );
}

const QgsEditorWidgetConfig QgsVectorLayer::editorWidgetV2Config( int fieldIdx )
{
return mEditorWidgetV2Configs.value( fieldIdx );
}

QString QgsVectorLayer::attributeAlias( int attributeIndex ) const
{
if ( attributeIndex < 0 || attributeIndex >= pendingFields().count() )
Expand Down Expand Up @@ -3006,6 +3019,16 @@ void QgsVectorLayer::setEditorLayout( EditorLayout editorLayout )
mEditorLayout = editorLayout;
}

void QgsVectorLayer::setEditorWidgetV2( int attrIdx, const QString& widgetType )
{
mEditorWidgetV2Types[ attrIdx ] = widgetType;
}

void QgsVectorLayer::setEditorWidgetV2Config( int attrIdx, const QMap<QString, QVariant>& config )
{
mEditorWidgetV2Configs[ attrIdx ] = config;
}

QString QgsVectorLayer::editForm()
{
return mEditForm;
Expand Down Expand Up @@ -3842,19 +3865,40 @@ QDomElement QgsAttributeEditorContainer::toDomElement( QDomDocument& doc ) const
{
QDomElement elem = doc.createElement( "attributeEditorContainer" );
elem.setAttribute( "name", mName );
for ( QList< QgsAttributeEditorElement* >::const_iterator it = mChildren.begin(); it != mChildren.end(); ++it )

Q_FOREACH( QgsAttributeEditorElement* child, mChildren )
{
elem.appendChild(( *it )->toDomElement( doc ) );
elem.appendChild( child->toDomElement( doc ) );
}
return elem;
}


void QgsAttributeEditorContainer::addChildElement( QgsAttributeEditorElement *widget )
{
mChildren.append( widget );
}

QList<QgsAttributeEditorElement*> QgsAttributeEditorContainer::findElements( QgsAttributeEditorElement::AttributeEditorType type ) const
{
QList<QgsAttributeEditorElement*> results;

Q_FOREACH( QgsAttributeEditorElement* elem, mChildren )
{
if ( elem->type() == type )
{
results.append( elem );
}

if ( elem->type() == AeTypeContainer )
{
QgsAttributeEditorContainer* cont = dynamic_cast<QgsAttributeEditorContainer*>( elem );
results += cont->findElements( type );
}
}

return results;
}

QDomElement QgsAttributeEditorField::toDomElement( QDomDocument& doc ) const
{
QDomElement elem = doc.createElement( "attributeEditorField" );
Expand Down
102 changes: 72 additions & 30 deletions src/core/qgsvectorlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "qgsmaplayer.h"
#include "qgsfeature.h"
#include "qgsfeatureiterator.h"
#include "qgseditorwidgetconfig.h"
#include "qgsfield.h"
#include "qgssnapper.h"
#include "qgsfield.h"
Expand All @@ -36,11 +37,12 @@ class QImage;

class QgsAttributeAction;
class QgsCoordinateTransform;
class QgsEditorWidgetWrapper;
class QgsFeatureRequest;
class QgsGeometry;
class QgsGeometryVertexIndex;
class QgsMapToPixel;
class QgsLabel;
class QgsMapToPixel;
class QgsRectangle;
class QgsVectorDataProvider;
class QgsSingleSymbolRendererV2;
Expand Down Expand Up @@ -69,7 +71,6 @@ class CORE_EXPORT QgsAttributeEditorElement : public QObject
AeTypeInvalid
};


QgsAttributeEditorElement( AttributeEditorType type, QString name, QObject *parent = NULL )
: QObject( parent ), mType( type ), mName( name ) {}

Expand All @@ -96,7 +97,9 @@ class CORE_EXPORT QgsAttributeEditorContainer : public QgsAttributeEditorElement

virtual QDomElement toDomElement( QDomDocument& doc ) const;
virtual void addChildElement( QgsAttributeEditorElement *widget );
virtual bool isGroupBox() const { return true; }
QList<QgsAttributeEditorElement*> children() const { return mChildren; }
virtual QList<QgsAttributeEditorElement*> findElements( AttributeEditorType type ) const;

private:
QList<QgsAttributeEditorElement*> mChildren;
Expand Down Expand Up @@ -431,19 +434,20 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
Classification,
EditRange,
SliderRange,
CheckBox, /**< @note added in 1.4 */
CheckBox, /**< @note added in 1.4 */
FileName,
Enumeration,
Immutable, /**< The attribute value should not be changed in the attribute form */
Hidden, /**< The attribute value should not be shown in the attribute form @note added in 1.4 */
TextEdit, /**< multiline edit @note added in 1.4*/
Calendar, /**< calendar widget @note added in 1.5 */
DialRange, /**< dial range @note added in 1.5 */
ValueRelation, /**< value map from an table @note added in 1.8 */
UuidGenerator, /**< uuid generator - readonly and automatically intialized @note added in 1.9 */
Photo, /**< phote widget @note added in 1.9 */
WebView, /**< webview widget @note added in 1.9 */
Color, /**< color @note added in 1.9 */
Immutable, /**< The attribute value should not be changed in the attribute form */
Hidden, /**< The attribute value should not be shown in the attribute form @note added in 1.4 */
TextEdit, /**< multiline edit @note added in 1.4*/
Calendar, /**< calendar widget @note added in 1.5 */
DialRange, /**< dial range @note added in 1.5 */
ValueRelation, /**< value map from an table @note added in 1.8 */
UuidGenerator, /**< uuid generator - readonly and automatically intialized @note added in 1.9 */
Photo, /**< phote widget @note added in 1.9 */
WebView, /**< webview widget @note added in 1.9 */
Color, /**< color @note added in 1.9 */
EditorWidgetV2, /**< modularized edit widgets @note added in 2.1 */
};

/** Types of feature form suppression after feature creation
Expand Down Expand Up @@ -1034,27 +1038,57 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
@note added in version 1.2 */
bool addAttribute( const QgsField &field );

/**Sets an alias (a display name) for attributes to display in dialogs
@note added in version 1.2*/
/**
* Sets an alias (a display name) for attributes to display in dialogs
* @note added in version 1.2
*/
void addAttributeAlias( int attIndex, QString aliasString );

/**Adds a tab (for the attribute editor form) holding groups and fields
@note added in version 1.9*/
/**
* Adds a tab (for the attribute editor form) holding groups and fields
* @note added in version 2.0
*/
void addAttributeEditorWidget( QgsAttributeEditorElement* data );

/**Returns a list of tabs holding groups and fields
@note added in version 1.9*/
/**
* 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
*/
const QString editorWidgetV2( int fieldIdx );

/**
* 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 id for the editor widget or a NULL string if not configured
*/
const QgsEditorWidgetConfig editorWidgetV2Config( int fieldIdx );

/**
* Returns a list of tabs holding groups and fields
* @note added in version 2.0
*/
QList< QgsAttributeEditorElement* > &attributeEditorElements();
/**Clears all the tabs for the attribute editor form
@note added in version 1.9*/
/**
* Clears all the tabs for the attribute editor form
* @note added in version 2.0
*/
void clearAttributeEditorWidgets();

/**Returns the alias of an attribute name or an empty string if there is no alias
@note added in version 1.2*/
/**
* Returns the alias of an attribute name or an empty string if there is no alias
* @note added in version 1.2
*/
QString attributeAlias( int attributeIndex ) const;

/**Convenience function that returns the attribute alias if defined or the field name else
@note added in version 1.2*/
/**
* Convenience function that returns the attribute alias if defined or the field name else
* @note added in version 1.2
*/
QString attributeDisplayName( int attributeIndex ) const;

const QMap< QString, QString >& attributeAliases() const { return mAttributeAliasMap; }
Expand Down Expand Up @@ -1118,6 +1152,10 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
/** set the active layout for the attribute editor for this layer (added in 1.9) */
void setEditorLayout( EditorLayout editorLayout );

void setEditorWidgetV2( int attrIdx, const QString& widgetType );

void setEditorWidgetV2Config( int attrIdx, const QMap<QString, QVariant>& config );

/** set string representing 'true' for a checkbox (added in 1.4) */
void setCheckedState( int idx, QString checked, QString notChecked );

Expand Down Expand Up @@ -1268,6 +1306,12 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
*/
int layerTransparency() const;

QString metadata();

/** @note not available in python bindings */
inline QgsGeometryCache* cache() { return mCache; }


public slots:
/**
* Select feature by its ID
Expand Down Expand Up @@ -1323,11 +1367,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
@note added in 1.7 */
void checkJoinLayerRemove( QString theLayerId );

QString metadata();

/** @note not available in python bindings */
inline QgsGeometryCache* cache() { return mCache; }

/**
* @brief Is called when the cache image is being deleted. Overwrite and use to clean up.
* @note added in 2.0
Expand Down Expand Up @@ -1564,6 +1603,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
QMap< QString, QString> mDateFormats;
QMap< QString, QSize> mWidgetSize;

QMap<int, QString> mEditorWidgetV2Types;
QMap<int, QMap<QString, QVariant> > mEditorWidgetV2Configs;

/** Defines the default layout to use for the attribute editor (Drag and drop, UI File, Generated) */
EditorLayout mEditorLayout;

Expand Down
18 changes: 18 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ attributetable/qgsfeaturelistviewdelegate.cpp
attributetable/qgsfeatureselectionmodel.cpp
attributetable/qgsdualview.cpp

editorwidgets/core/qgseditorconfigwidget.cpp
editorwidgets/core/qgseditorwidgetfactory.cpp
editorwidgets/core/qgseditorwidgetregistry.cpp
editorwidgets/core/qgseditorwidgetwrapper.cpp

qgisgui.cpp
qgisinterface.cpp
qgsannotationitem.cpp
Expand Down Expand Up @@ -97,6 +102,7 @@ qgsnewvectorlayerdialog.cpp
qgsnumericsortlistviewitem.cpp
qgsoptionsdialogbase.cpp
qgscredentialdialog.cpp
qgsattributeeditorcontext.cpp
qgsowssourceselect.cpp
qgsprojectbadlayerguihandler.cpp
qgsprojectionselector.cpp
Expand Down Expand Up @@ -177,6 +183,10 @@ attributetable/qgsfeatureselectionmodel.h
attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsdualview.h

editorwidgets/core/qgseditorconfigwidget.h
editorwidgets/core/qgseditorwidgetregistry.h
editorwidgets/core/qgseditorwidgetwrapper.h

qgsattributedialog.h
qgsattributeeditor.h
qgsblendmodecombobox.h
Expand Down Expand Up @@ -236,6 +246,7 @@ QT4_WRAP_CPP(QGIS_GUI_MOC_SRCS ${QGIS_GUI_MOC_HDRS})
SET(QGIS_GUI_HDRS
qgisgui.h
qgisinterface.h
qgsattributeeditorcontext.h
qgsbusyindicatordialog.h
qgscharacterselectdialog.h
qgscolordialog.h
Expand Down Expand Up @@ -296,6 +307,11 @@ attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsfeatureselectionmodel.h
attributetable/qgsdualview.h

editorwidgets/core/qgseditorconfigwidget.h
editorwidgets/core/qgseditorwidgetfactory.h
editorwidgets/core/qgseditorwidgetregistry.h
editorwidgets/core/qgseditorwidgetwrapper.h

raster/qgsrasterrendererwidget.h
)

Expand Down Expand Up @@ -339,6 +355,8 @@ INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/symbology-ng
${CMAKE_CURRENT_SOURCE_DIR}/attributetable
${CMAKE_CURRENT_SOURCE_DIR}/editorwidgets
${CMAKE_CURRENT_SOURCE_DIR}/editorwidgets/core
../core
../core/composer
../core/raster
Expand Down
36 changes: 36 additions & 0 deletions src/gui/editorwidgets/core/qgseditorconfigwidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/***************************************************************************
qgseditorconfigwidget.cpp
--------------------------------------
Date : 24.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 "qgseditorconfigwidget.h"


QgsEditorConfigWidget::QgsEditorConfigWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent )
: QWidget( parent )
, mLayer( vl )
, mField( fieldIdx )

{
}

int QgsEditorConfigWidget::field()
{
return mField;
}

QgsVectorLayer*QgsEditorConfigWidget::layer()
{
return mLayer;
}

84 changes: 84 additions & 0 deletions src/gui/editorwidgets/core/qgseditorconfigwidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/***************************************************************************
qgseditorconfigwidget.h
--------------------------------------
Date : 24.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 QGSEDITORCONFIGWIDGET_H
#define QGSEDITORCONFIGWIDGET_H

#include <QWidget>

#include "qgseditorwidgetwrapper.h"

class QgsVectorLayer;

/**
* This class should be subclassed for every configurable editor widget type.
*
* It implements the GUI configuration widget and transforms this to/from a configuration.
*
* It will only be instantiated by {@see QgsEditorWidgetFactory}
*/

class GUI_EXPORT QgsEditorConfigWidget : public QWidget
{
Q_OBJECT
public:
/**
* Create a new configuration widget
*
* @param vl The layer for which the configuration dialog will be created
* @param fieldIdx The index of the field on the layer for which this dialog will be created
* @param parent A parent widget
*/
explicit QgsEditorConfigWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent );

/**
* @brief Create a configuration from the current GUI state
*
* @return A widget configuration
*/
virtual QgsEditorWidgetConfig config() = 0;

/**
* @brief Update the configuration widget to represent the given configuration.
*
* @param config The configuration which should be represented by this widget
*/
virtual void setConfig( const QgsEditorWidgetConfig& config ) = 0;

/**
* Returns the field for which this configuration widget applies
*
* @return The field index
*/
int field();

/**
* Returns the layer for which this configuration widget applies
*
* @return The layer
*/
QgsVectorLayer* layer();

/**
* Destructor
*/
virtual ~QgsEditorConfigWidget() {}

private:
QgsVectorLayer* mLayer;
int mField;
};

#endif // QGSEDITORCONFIGWIDGET_H
49 changes: 49 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/***************************************************************************
qgseditorwidgetfactory.cpp
--------------------------------------
Date : 21.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 "qgseditorwidgetfactory.h"

QgsEditorWidgetFactory::QgsEditorWidgetFactory( const QString& name )
: mName( name )
{
}

QgsEditorWidgetFactory::~QgsEditorWidgetFactory()
{
}

QString QgsEditorWidgetFactory::name()
{
return mName;
}

QgsEditorWidgetConfig QgsEditorWidgetFactory::readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx )
{
Q_UNUSED( configElement );
Q_UNUSED( layer );
Q_UNUSED( fieldIdx );

return QgsEditorWidgetConfig();
}

void QgsEditorWidgetFactory::writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, const QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx )
{
Q_UNUSED( config );
Q_UNUSED( configElement );
Q_UNUSED( doc );
Q_UNUSED( layer );
Q_UNUSED( fieldIdx );
}

163 changes: 163 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/***************************************************************************
qgseditorwidgetfactory.h
--------------------------------------
Date : 21.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 QGSEDITORWIDGETFACTORY_H
#define QGSEDITORWIDGETFACTORY_H

#include "qgseditorwidgetwrapper.h"
#include "qgsapplication.h"

#include <QDomNode>
#include <QMap>
#include <QString>

class QgsEditorConfigWidget;

/**
* Every attribute editor widget needs a factory, which inherits this class
*
* It provides metadata for the widgets such as the name (human readable), it serializes
* the configuration to an xml structure and loads the configuration from there.
*
* It also has factory methods to create a widget wrapper for the attribute editor itself
* and another factory method to create a configuration dialog.
*/
class GUI_EXPORT QgsEditorWidgetFactory
{
public:
/**
* Constructor
*
* @param name A human readable name for this widget type
*/
QgsEditorWidgetFactory( const QString& name );

virtual ~QgsEditorWidgetFactory();

/**
* Override this in your implementation.
* Create a new editor widget wrapper. Call {@link QgsEditorWidgetRegistry::create()}
* instead of calling this method directly.
*
* @param vl The vector layer on which this widget will act
* @param fieldIdx The field index on which this widget will act
* @param editor An editor widget if already existent. If NULL is provided, a new widget will be created.
* @param parent The parent for the wrapper class and any created widget.
*
* @return A new widget wrapper
*/
virtual QgsEditorWidgetWrapper* create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ) const = 0;

/**
* Return The human readable name of this widget type
*
* By default returns the name specified when constructing and does not need to be overwritten
*
* @return a name
*/
virtual QString name();

/**
* Override this in your implementation.
* Create a new configuration widget for this widget type.
*
* @param vl The layer for which the widget will be created
* @param fieldIdx The field index for which the widget will be created
* @param parent The parent widget of the created config widget
*
* @return A configuration widget
*/
virtual QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent ) const = 0;

/**
* Read the config from an XML file and map it to a proper {@link QgsEditorWidgetConfig}.
*
* @param configElement The configuration element from the project file
* @param layer The layer for which this configuration applies
* @param fieldIdx The field on the layer for which this configuration applies
*
* @return A configuration object. This will be passed to your widget wrapper later on
*/
virtual QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx );

/**
* Serialize your configuration and save it in a xml doc.
*
* @param config The configuration to serialize
* @param configElement The element, where you can write your configuration into
* @param doc The document. You can use this to create new nodes
* @param layer The layer for which this configuration applies
* @param fieldIdx The field on the layer for which this configuration applies
*/
virtual void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, const QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx );

private:
QString mName;
};

/**
* This is a templated wrapper class, which inherits QgsEditWidgetFactory and does the boring work for you.
* C++ only
*/
template<typename F, typename G>
class GUI_EXPORT QgsEditWidgetFactoryHelper : public QgsEditorWidgetFactory
{
public:
QgsEditWidgetFactoryHelper( QString name )
: QgsEditorWidgetFactory( name ) {}

QgsEditorWidgetWrapper* create( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent ) const
{
return new F( vl, fieldIdx, editor, parent );
}

QgsEditorConfigWidget* configWidget( QgsVectorLayer* vl, int fieldIdx, QWidget* parent )
{
return new G( vl, fieldIdx, parent );
}

/**
* Read the config from an XML file and map it to a proper {@link QgsEditorWidgetConfig}.
*
* Implement this method yourself somewhere with the class template parameters
* specified. To keep things clean, every implementation of this class should be placed
* next to the associated widget factory implementation.
*
* @param configElement The configuration element from the project file
* @param layer The layer for which this configuration applies
* @param fieldIdx The field on the layer for which this configuration applies
*
* @return A configuration object. This will be passed to your widget wrapper later on
*/

virtual QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx );

/**
* Serialize your configuration and save it in a xml doc.
*
* Implement this method yourself somewhere with the class template parameters
* specified. To keep things clean, every implementation of this class should be placed
* next to the associated widget factory implementation.
*
* @param config The configuration to serialize
* @param configElement The element, where you can write your configuration into
* @param doc The document. You can use this to create new nodes
* @param layer The layer for which this configuration applies
* @param fieldIdx The field on the layer for which this configuration applies
*/
virtual void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, const QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx );
};

#endif // QGSEDITORWIDGETFACTORY_H
218 changes: 218 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/***************************************************************************
qgseditorwidgetregistry.cpp
--------------------------------------
Date : 24.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 "qgseditorwidgetregistry.h"

#include "qgseditorwidgetfactory.h"
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include "qgsmessagelog.h"


QgsEditorWidgetRegistry QgsEditorWidgetRegistry::sInstance;

QgsEditorWidgetRegistry* QgsEditorWidgetRegistry::instance()
{
return &sInstance;
}

QgsEditorWidgetRegistry::QgsEditorWidgetRegistry()
{
connect( QgsProject::instance(), SIGNAL( readMapLayer( QgsMapLayer*, const QDomElement& ) ), this, SLOT( readMapLayer( QgsMapLayer*, const QDomElement& ) ) );
connect( QgsProject::instance(), SIGNAL( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ), this, SLOT( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ) );
}

QgsEditorWidgetRegistry::~QgsEditorWidgetRegistry()
{
qDeleteAll( mWidgetFactories.values() );
}

QgsEditorWidgetWrapper* QgsEditorWidgetRegistry::create( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, QWidget* editor, QWidget* parent )
{
if ( mWidgetFactories.contains( widgetId ) )
{
QgsEditorWidgetWrapper* ww = mWidgetFactories[widgetId]->create( vl, fieldIdx, editor, parent );
if ( ww )
{
ww->setConfig( config );
return ww;
}
}
return 0;
}

QgsEditorConfigWidget* QgsEditorWidgetRegistry::createConfigWidget( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, QWidget* parent )
{
if ( mWidgetFactories.contains( widgetId ) )
{
return mWidgetFactories[widgetId]->configWidget( vl, fieldIdx, parent );
}
return 0;
}

QString QgsEditorWidgetRegistry::name( const QString& widgetId )
{
if ( mWidgetFactories.contains( widgetId ) )
{
return mWidgetFactories[widgetId]->name();
}

return QString();
}

const QMap<QString, QgsEditorWidgetFactory*> QgsEditorWidgetRegistry::factories()
{
return mWidgetFactories;
}

bool QgsEditorWidgetRegistry::registerWidget( const QString& widgetId, QgsEditorWidgetFactory* widgetFactory )
{
if ( !widgetFactory )
{
QgsMessageLog::instance()->logMessage( "QgsEditorWidgetRegistry: Factory not valid." );
return false;
}
else if ( mWidgetFactories.contains( widgetId ) )
{
QgsMessageLog::instance()->logMessage( QString( "QgsEditorWidgetRegistry: Factory with id %1 already registered." ).arg( widgetId ) );
return false;
}
else
{
mWidgetFactories.insert( widgetId, widgetFactory );
return true;
}
}

void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomElement& layerElem )
{
if ( mapLayer->type() != QgsMapLayer::VectorLayer )
{
return;
}

QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
if ( !vectorLayer )
{
return;
}

for ( int idx = 0; idx < vectorLayer->pendingFields().count(); ++idx )
{
if ( vectorLayer->editType( idx ) != QgsVectorLayer::EditorWidgetV2 )
{
continue;
}

QDomNodeList editTypeNodes = layerElem.namedItem( "edittypes" ).childNodes();

for ( int i = 0; i < editTypeNodes.size(); i++ )
{
QDomNode editTypeNode = editTypeNodes.at( i );
QDomElement editTypeElement = editTypeNode.toElement();

QString name = editTypeElement.attribute( "name" );

if ( vectorLayer->fieldNameIndex( name ) != idx )
continue;

QgsVectorLayer::EditType editType =
( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt();

if ( editType != QgsVectorLayer::EditorWidgetV2 )
continue;

QString ewv2Type = editTypeElement.attribute( "widgetv2type" );

if ( mWidgetFactories.contains( ewv2Type ) )
{
vectorLayer->setEditorWidgetV2( idx, ewv2Type );
QDomElement ewv2CfgElem = editTypeElement.namedItem( "widgetv2config" ).toElement();

if ( !ewv2CfgElem.isNull() )
{
QMap<QString, QVariant> cfg = mWidgetFactories[ewv2Type]->readConfig( ewv2CfgElem, vectorLayer, idx );
vectorLayer->setEditorWidgetV2Config( idx, cfg );
}
}
else
{
QgsMessageLog::logMessage( tr( "Unknown attribute editor widget '%1'" ).arg( ewv2Type ) );
}
}
}
}

void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement& layerElem, QDomDocument& doc )
{
if ( mapLayer->type() != QgsMapLayer::VectorLayer )
{
return;
}

QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
if ( !vectorLayer )
{
return;
}

for ( int idx = 0; idx < vectorLayer->pendingFields().count(); ++idx )
{
if ( vectorLayer->editType( idx ) != QgsVectorLayer::EditorWidgetV2 )
{
continue;
}

const QString& widgetType = vectorLayer->editorWidgetV2( idx );
if ( !mWidgetFactories.contains( widgetType ) )
{
QgsMessageLog::logMessage( tr( "Could not save unknown editor widget type '%1'." ).arg( widgetType ) );
continue;
}

QDomNodeList editTypeNodes = layerElem.namedItem( "edittypes" ).childNodes();

for ( int i = 0; i < editTypeNodes.size(); i++ )
{
QDomElement editTypeElement = editTypeNodes.at( i ).toElement();

QString name = editTypeElement.attribute( "name" );

if ( vectorLayer->fieldNameIndex( name ) != idx )
continue;

QgsVectorLayer::EditType editType =
( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt();

if ( editType != QgsVectorLayer::EditorWidgetV2 )
continue;

editTypeElement.setAttribute( "widgetv2type", widgetType );

if ( mWidgetFactories.contains( widgetType ) )
{
QDomElement ewv2CfgElem = doc.createElement( "widgetv2config" );

mWidgetFactories[widgetType]->writeConfig( vectorLayer->editorWidgetV2Config( idx ), ewv2CfgElem, doc, vectorLayer, idx );

editTypeElement.appendChild( ewv2CfgElem );
}
else
{
QgsMessageLog::logMessage( tr( "Unknown attribute editor widget '%1'" ).arg( widgetType ) );
}
}
}
}
119 changes: 119 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetregistry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/***************************************************************************
qgseditorwidgetregistry.h
--------------------------------------
Date : 24.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 QGSEDITORWIDGETREGISTRY_H
#define QGSEDITORWIDGETREGISTRY_H

#include <QObject>
#include <QMap>

#include "qgseditorwidgetfactory.h"

class QgsMapLayer;
class QDomNode;

/**
* This class manages all known edit widget factories
*/
class GUI_EXPORT QgsEditorWidgetRegistry : public QObject
{
Q_OBJECT

public:
/**
* This class is a singleton and has therefore to be accessed with this method instead
* of a constructor.
*
* @return
*/
static QgsEditorWidgetRegistry* instance();
~QgsEditorWidgetRegistry();

/**
* 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.
*
* @param widgetId The id of the widget type to create an attribute editor for
* @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 config A configuration which should be used for the widget creation
* @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
*
* @return A new widget wrapper
*/
QgsEditorWidgetWrapper* create( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, QWidget* editor, QWidget* parent );

/**
* Creates a configuration widget
*
* @param widgetId The id of the widget type to create a configuration widget for
* @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 parent The parent widget for the created widget
*
* @return A new configuration widget
*/
QgsEditorConfigWidget* createConfigWidget( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, QWidget* parent );

/**
* Get the human readable name for a widget type
*
* @param widgetId The widget type to get the name for
*
* @return A human readable name
*/
QString name( const QString& widgetId );

/**
* Get access to all registered factories
*
* @return All ids and factories
*/
const QMap<QString, QgsEditorWidgetFactory*> factories();

/**
* The other part which does the boring work for you
*/
template <class W, class C>
void registerWidget( const QString& widgetType, const QString& name )
{
mWidgetFactories.insert( widgetType, new QgsEditWidgetFactoryHelper<W, C>( name ) );
}

/**
* Register a new widget factory with the given id
*
* @param widgetId The id which will be used later to refer to this widget type
* @param widgetFactory The factory which will create this widget type
*
* @return true, if successful, false, if the widgetId is already in use or widgetFactory is NULL
*/
bool registerWidget( const QString& widgetId, QgsEditorWidgetFactory* widgetFactory );

protected:
QgsEditorWidgetRegistry();

private slots:
void readMapLayer( QgsMapLayer* mapLayer , const QDomElement& layerElem );
void writeMapLayer( QgsMapLayer* mapLayer , QDomElement& layerElem, QDomDocument& doc );

private:
QMap<QString, QgsEditorWidgetFactory*> mWidgetFactories;
static QgsEditorWidgetRegistry sInstance;
};


#endif // QGSEDITORWIDGETREGISTRY_H
94 changes: 94 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/***************************************************************************
qgseditorwidgetwrapper.cpp
--------------------------------------
Date : 20.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 "qgseditorwidgetwrapper.h"

#include <QWidget>

QgsEditorWidgetWrapper::QgsEditorWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent )
: QObject( parent )
, mWidget( editor )
, mParent( parent )
, mLayer( vl )
{
mField = fieldIdx;
}

QWidget* QgsEditorWidgetWrapper::widget()
{
if ( !mWidget )
{
mWidget = createWidget( mParent );
mWidget->setProperty( "EWV2Wrapper", QVariant::fromValue( this ) );
initWidget( mWidget );
}

return mWidget;
}

void QgsEditorWidgetWrapper::setConfig( const QgsEditorWidgetConfig& config )
{
mConfig = config;
// If an editor widget was supplied, we can initialize this now
if ( mWidget )
{
mWidget->setProperty( "EWV2Wrapper", QVariant::fromValue( this ) );
initWidget( mWidget );
}
}

QVariant QgsEditorWidgetWrapper::config( QString key, QVariant defaultVal )
{
if ( mConfig.contains( key ) )
{
return mConfig[key];
}
return defaultVal;
}

QgsVectorLayer* QgsEditorWidgetWrapper::layer()
{
return mLayer;
}

int QgsEditorWidgetWrapper::field()
{
return mField;
}

QgsEditorWidgetWrapper* QgsEditorWidgetWrapper::fromWidget( QWidget* widget )
{
QVariant w = widget->property( "EWV2Wrapper" );

if ( w.isNull() )
{
return NULL;
}

return w.value<QgsEditorWidgetWrapper*>();
}

void QgsEditorWidgetWrapper::initWidget( QWidget* editor )
{
Q_UNUSED( editor )
}

void QgsEditorWidgetWrapper::setEnabled( bool enabled )
{
if ( mWidget )
{
mWidget->setEnabled( enabled );
}
}
181 changes: 181 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetwrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/***************************************************************************
qgseditorwidgetwrapper.h
--------------------------------------
Date : 20.4.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 QGSEDITORWIDGETWRAPPER_H
#define QGSEDITORWIDGETWRAPPER_H

#include <QObject>
#include <QMap>
#include <QVariant>

class QgsVectorLayer;

#include "qgseditorwidgetconfig.h"

/**
* Manages an editor widget
* Widget and wrapper share the same parent
*
* A wrapper controls one attribute editor widget and is able to create a default
* widget or use a pre-existent widget. It is able to set the widget to the value implied
* by a field of a vector layer, or return the value it currently holds. Every time it is changed
* it has to emit a valueChanged signal (this does not yet mean, that the value is accepted).
*
*/
class GUI_EXPORT QgsEditorWidgetWrapper : public QObject
{
Q_OBJECT
public:
/**
* Create a new widget wrapper
*
* @param vl The layer on which the field is
* @param fieldIdx The field which will be controlled
* @param editor An editor widget. Can be NULL if one should be autogenerated.
* @param parent A parent widget for this widget wrapper and the created widget.
*/
explicit QgsEditorWidgetWrapper( QgsVectorLayer* vl, int fieldIdx, QWidget* editor = 0, QWidget* parent = 0 );

/**
* Will be used to access the widget's value. Read the value from the widget and
* return it properly formatted to be saved in the attribute.
*
* @return The current value the widget represents
*/
virtual QVariant value() = 0;

/**
* @brief Access the widget managed by this wrapper
*
* @return The widget
*/
QWidget* widget();

/**
* @brief Access the widget managed by this wrapper and cast it to a given type
* Example: QPushButton* pb = wrapper->widget<QPushButton*>();
*
* @return The widget as template type or NULL, if it cannot be cast to this type.
*/
template <class T>
T* widget() { return dynamic_cast<T>( mWidget ); }

/**
* Will set the config of this wrapper to the specified config.
*
* @param config The config for this wrapper
*/
void setConfig( const QgsEditorWidgetConfig& config );

/**
* Use this inside your overriden classes to access the configuration.
*
* @param key The configuration option you want to load
* @param defaultVal Default value
*
* @return the value assigned to this configuration option
*/
QVariant config( QString key, QVariant defaultVal = QVariant() );

/**
* Returns the whole config
*
* @return The configuration
*/
const QgsEditorWidgetConfig config();

/**
* Access the QgsVectorLayer, you are working on
*
* @return The layer
*
* @see field()
*/
QgsVectorLayer* layer();

/**
* Access the field index.
*
* @return The index of the field you are working on
*
* @see layer()
*/
int field();

/**
* Will return a wrapper for a given widget
* @param widget The widget which was created by a wrapper
* @return The wrapper for the widget or NULL
*/
static QgsEditorWidgetWrapper* fromWidget( QWidget* widget );

protected:
/**
* This method should create a new widget with the provided parent. This will only be called
* if the form did not already provide a widget, so it is not guaranteed to be called!
* You should not do initialisation stuff, which also has to be done for custom editor
* widgets inside this method. Things like filling comboboxes and assigning other data which
* will also be used to make widgets on forms created in the QtDesigner usable should be assigned
* in {@link initWidget(QWidget*)}.
*
* @param parent You should set this parent on the created widget.
* @return A new widget
*/
virtual QWidget* createWidget( QWidget* parent ) = 0;

/**
* This method should initialize the editor widget with runtime data. Fill your comboboxes here.
*
* @param editor The widget which will represent this attribute editor in a form.
*/
virtual void initWidget( QWidget* editor );

signals:
/**
* Emit this signal, whenever the value changed.
*
* @param value The new value
*/
void valueChanged( const QVariant& value );

public slots:
/**
* Is called, when the value of the widget needs to be changed. Update the widget representation
* to reflect the new value.
*
* @param value The new value of the attribute
*/
virtual void setValue( const QVariant& value ) = 0;

/**
* Is used to enable or disable the edit functionality of the managed widget.
* By default this will enable or disable the whole widget
*
* @param enabled Enable or Disable?
*/
virtual void setEnabled( bool enabled );

private:
QgsEditorWidgetConfig mConfig;
QWidget* mWidget;
QWidget* mParent;
QgsVectorLayer* mLayer;
int mField;
};

// We'll use this class inside a QVariant in the widgets properties
Q_DECLARE_METATYPE( QgsEditorWidgetWrapper* )

#endif // QGSEDITORWIDGETWRAPPER_H
131 changes: 85 additions & 46 deletions src/gui/qgsattributedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* *
***************************************************************************/
#include "qgsattributedialog.h"
#include "qgseditorwidgetwrapper.h"
#include "qgsfield.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
Expand Down Expand Up @@ -42,52 +43,73 @@
#include <QWebView>
#include <QPushButton>

int QgsAttributeDialog::smFormCounter = 0;
int QgsAttributeDialog::sFormCounter = 0;
QString QgsAttributeDialog::sSettingsPath = "/Windows/AttributeDialog/";

QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QgsDistanceArea myDa, QWidget* parent, bool showDialogButtons )
QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer* vl, QgsFeature* thepFeature, bool featureOwner, QWidget* parent, bool showDialogButtons, QgsAttributeEditorContext context )
: QObject( parent )
, mDialog( 0 )
, mSettingsPath( "/Windows/AttributeDialog/" )
, mContext( context )
, mLayer( vl )
, mFeature( thepFeature )
, mFeatureOwner( featureOwner )
, mHighlight( 0 )
, mFormNr( -1 )
, mFormNr( sFormCounter++ )
, mShowDialogButtons( showDialogButtons )
{
if ( !mFeature || !vl->dataProvider() )
mContext.adjustForLayer( mLayer );
init();
}

QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer* vl, QgsFeature* thepFeature, bool featureOwner, QgsDistanceArea myDa, QWidget* parent, bool showDialogButtons )
: QObject( parent )
, mDialog( 0 )
, mContext( )
, mLayer( vl )
, mFeature( thepFeature )
, mFeatureOwner( featureOwner )
, mHighlight( 0 )
, mFormNr( sFormCounter++ )
, mShowDialogButtons( showDialogButtons )
{
mContext.setDistanceArea( myDa );
mContext.adjustForLayer( mLayer );
init();
}

void QgsAttributeDialog::init()
{
if ( !mFeature || !mLayer->dataProvider() )
return;

const QgsFields &theFields = vl->pendingFields();
const QgsFields &theFields = mLayer->pendingFields();
if ( theFields.isEmpty() )
return;

QgsAttributes myAttributes = mFeature->attributes();

QDialogButtonBox *buttonBox = NULL;

if ( vl->editorLayout() == QgsVectorLayer::UiFileLayout && !vl->editForm().isEmpty() )
if ( mLayer->editorLayout() == QgsVectorLayer::UiFileLayout && !mLayer->editForm().isEmpty() )
{
// UI-File defined layout
QFile file( vl->editForm() );
QFile file( mLayer->editForm() );

if ( file.open( QFile::ReadOnly ) )
{
QUiLoader loader;

QFileInfo fi( vl->editForm() );
QFileInfo fi( mLayer->editForm() );
loader.setWorkingDirectory( fi.dir() );
QWidget *myWidget = loader.load( &file, parent );
QWidget *myWidget = loader.load( &file, qobject_cast<QWidget*>( parent() ) );
file.close();

mDialog = qobject_cast<QDialog*>( myWidget );
buttonBox = myWidget->findChild<QDialogButtonBox*>();
}
}
else if ( vl->editorLayout() == QgsVectorLayer::TabLayout )
else if ( mLayer->editorLayout() == QgsVectorLayer::TabLayout )
{
// Tab display
mDialog = new QDialog( parent );
mDialog = new QDialog( qobject_cast<QWidget*>( parent() ) );

QGridLayout *gridLayout;
QTabWidget *tabWidget;
Expand All @@ -99,7 +121,7 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
tabWidget = new QTabWidget( mDialog );
gridLayout->addWidget( tabWidget );

foreach ( const QgsAttributeEditorElement *widgDef, vl->attributeEditorElements() )
foreach ( const QgsAttributeEditorElement *widgDef, mLayer->attributeEditorElements() )
{
QWidget* tabPage = new QWidget( tabWidget );

Expand All @@ -108,7 +130,9 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat

if ( widgDef->type() == QgsAttributeEditorElement::AeTypeContainer )
{
tabPageLayout->addWidget( QgsAttributeEditor::createWidgetFromDef( widgDef, tabPage, vl, myAttributes, mProxyWidgets, false ) );
QString dummy1;
bool dummy2;
tabPageLayout->addWidget( QgsAttributeEditor::createWidgetFromDef( widgDef, tabPage, mLayer, *mFeature, mContext, dummy1, dummy2 ) );
}
else
{
Expand All @@ -121,9 +145,10 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
gridLayout->addWidget( buttonBox );
}

// Still no dialog: create the default generated dialog
if ( !mDialog )
{
mDialog = new QDialog( parent );
mDialog = new QDialog( qobject_cast<QWidget*>( parent() ) );

QGridLayout *gridLayout;
QFrame *mFrame;
Expand Down Expand Up @@ -174,35 +199,35 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
for ( int fldIdx = 0; fldIdx < theFields.count(); ++fldIdx )
{
//show attribute alias if available
QString myFieldName = vl->attributeDisplayName( fldIdx );
QString myFieldName = mLayer->attributeDisplayName( fldIdx );
// by default (until user defined alias) append date format
// (validator does not allow to enter a value in wrong format)
const QgsField &myField = theFields[fldIdx];
if ( myField.type() == QVariant::Date && vl->attributeAlias( fldIdx ).isEmpty() )
if ( myField.type() == QVariant::Date && mLayer->attributeAlias( fldIdx ).isEmpty() )
{
myFieldName += " (" + vl->dateFormat( fldIdx ) + ")";
myFieldName += " (" + mLayer->dateFormat( fldIdx ) + ")";
}

QWidget *myWidget = QgsAttributeEditor::createAttributeEditor( 0, 0, vl, fldIdx, myAttributes[fldIdx], mProxyWidgets );
QWidget *myWidget = QgsAttributeEditor::createAttributeEditor( mDialog, 0, mLayer, fldIdx, mFeature->attribute( fldIdx ), mContext );
if ( !myWidget )
continue;

QLabel *mypLabel = new QLabel( myFieldName, mypInnerFrame );

if ( vl->editType( fldIdx ) != QgsVectorLayer::Immutable )
if ( mLayer->editType( fldIdx ) != QgsVectorLayer::Immutable )
{
if ( vl->isEditable() && vl->fieldEditable( fldIdx ) )
if ( mLayer->isEditable() && mLayer->fieldEditable( fldIdx ) )
{
myWidget->setEnabled( true );
}
else if ( vl->editType( fldIdx ) == QgsVectorLayer::Photo )
else if ( mLayer->editType( fldIdx ) == QgsVectorLayer::Photo )
{
foreach ( QWidget *w, myWidget->findChildren<QWidget *>() )
{
w->setEnabled( qobject_cast<QLabel *>( w ) ? true : false );
}
}
else if ( vl->editType( fldIdx ) == QgsVectorLayer::WebView )
else if ( mLayer->editType( fldIdx ) == QgsVectorLayer::WebView )
{
foreach ( QWidget *w, myWidget->findChildren<QWidget *>() )
{
Expand All @@ -214,13 +239,21 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
w->setEnabled( false );
}
}
else if ( mLayer->editType( fldIdx ) == QgsVectorLayer::EditorWidgetV2 )
{
QgsEditorWidgetWrapper* ww = QgsEditorWidgetWrapper::fromWidget( myWidget );
if ( ww )
{
ww->setEnabled( false );
}
}
else
{
myWidget->setEnabled( false );
}
}

if ( vl->labelOnTop( fldIdx ) )
if ( mLayer->labelOnTop( fldIdx ) )
{
mypInnerLayout->addWidget( mypLabel, index++, 0, 1, 2 );
mypInnerLayout->addWidget( myWidget, index++, 0, 1, 2 );
Expand Down Expand Up @@ -261,28 +294,36 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat

foreach ( QWidget *myWidget, myWidgets )
{
QgsAttributeEditor::createAttributeEditor( mDialog, myWidget, vl, fldIdx, myAttributes[fldIdx], mProxyWidgets );
QgsAttributeEditor::createAttributeEditor( mDialog, myWidget, mLayer, fldIdx, mFeature->attribute( fldIdx ), mContext );

if ( vl->editType( fldIdx ) != QgsVectorLayer::Immutable )
if ( mLayer->editType( fldIdx ) != QgsVectorLayer::Immutable )
{
if ( vl->isEditable() && vl->fieldEditable( fldIdx ) )
if ( mLayer->isEditable() && mLayer->fieldEditable( fldIdx ) )
{
myWidget->setEnabled( true );
}
else if ( vl->editType( fldIdx ) == QgsVectorLayer::Photo )
else if ( mLayer->editType( fldIdx ) == QgsVectorLayer::Photo )
{
foreach ( QWidget *w, myWidget->findChildren<QWidget *>() )
{
w->setEnabled( qobject_cast<QLabel *>( w ) ? true : false );
}
}
else if ( vl->editType( fldIdx ) == QgsVectorLayer::WebView )
else if ( mLayer->editType( fldIdx ) == QgsVectorLayer::WebView )
{
foreach ( QWidget *w, myWidget->findChildren<QWidget *>() )
{
w->setEnabled( qobject_cast<QWebView *>( w ) ? true : false );
}
}
else if ( mLayer->editType( fldIdx ) == QgsVectorLayer::EditorWidgetV2 )
{
QgsEditorWidgetWrapper* ww = QgsEditorWidgetWrapper::fromWidget( myWidget );
if ( ww )
{
ww->setEnabled( false );
}
}
else
{
myWidget->setEnabled( false );
Expand All @@ -308,15 +349,15 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
if ( !mFeature->geometry() && exp.needsGeometry() )
{
QgsFeature f;
if ( vl->getFeatures( QgsFeatureRequest().setFilterFid( mFeature->id() ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) && f.geometry() )
if ( mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature->id() ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) && f.geometry() )
{
mFeature->setGeometry( *f.geometry() );
}
}

exp.setGeomCalculator( myDa );
exp.setGeomCalculator( mContext.distanceArea() );

QVariant value = exp.evaluate( mFeature, vl->pendingFields() );
QVariant value = exp.evaluate( mFeature, mLayer->pendingFields() );

if ( !exp.hasEvalError() )
{
Expand Down Expand Up @@ -344,7 +385,7 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
mDialog->setObjectName( "QgsAttributeDialogBase" );

if ( mDialog->windowTitle().isEmpty() )
mDialog->setWindowTitle( tr( "Attributes - %1" ).arg( vl->name() ) );
mDialog->setWindowTitle( tr( "Attributes - %1" ).arg( mLayer->name() ) );
}

if ( mShowDialogButtons )
Expand All @@ -353,7 +394,7 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
{
buttonBox->clear();

if ( vl->isEditable() )
if ( mLayer->isEditable() )
{
buttonBox->setStandardButtons( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
connect( buttonBox, SIGNAL( accepted() ), mDialog, SLOT( accept() ) );
Expand All @@ -379,15 +420,15 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat

connect( mDialog, SIGNAL( destroyed() ), this, SLOT( dialogDestroyed() ) );

if ( !vl->editFormInit().isEmpty() )
if ( !mLayer->editFormInit().isEmpty() )
{
#if 0
// would be nice if only PyQt's QVariant.toPyObject() wouldn't take ownership
vl->setProperty( "featureForm.dialog", QVariant::fromValue( qobject_cast<QObject*>( mDialog ) ) );
vl->setProperty( "featureForm.id", QVariant( mpFeature->id() ) );
#endif

QString module = vl->editFormInit();
QString module = mLayer->editFormInit();
int pos = module.lastIndexOf( "." );
if ( pos >= 0 )
{
Expand All @@ -402,15 +443,13 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat

QgsPythonRunner::run( reload );

mFormNr = smFormCounter++;

QString form = QString( "_qgis_featureform_%1 = sip.wrapinstance( %2, QtGui.QDialog )" )
.arg( mFormNr )
.arg(( unsigned long ) mDialog );

QString layer = QString( "_qgis_layer_%1 = sip.wrapinstance( %2, qgis.core.QgsVectorLayer )" )
.arg( vl->id() )
.arg(( unsigned long ) vl );
.arg( mLayer->id() )
.arg(( unsigned long ) mLayer );

// Generate the unique ID of this feature. We used to use feature ID but some providers
// return a ID that is an invalid python variable when we have new unsaved features.
Expand All @@ -426,9 +465,9 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat

mReturnvarname = QString( "_qgis_feature_form_%1" ).arg( dt.toString( "yyyyMMddhhmmsszzz" ) );
QString expr = QString( "%5 = %1(_qgis_featureform_%2, _qgis_layer_%3, %4)" )
.arg( vl->editFormInit() )
.arg( mLayer->editFormInit() )
.arg( mFormNr )
.arg( vl->id() )
.arg( mLayer->id() )
.arg( featurevarname )
.arg( mReturnvarname );

Expand All @@ -437,7 +476,7 @@ QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeat
}

// Only restore the geometry of the dialog if it's not a custom one.
if ( vl->editorLayout() != QgsVectorLayer::UiFileLayout )
if ( mLayer->editorLayout() != QgsVectorLayer::UiFileLayout )
{
restoreGeometry();
}
Expand Down Expand Up @@ -480,7 +519,7 @@ void QgsAttributeDialog::accept()
{
QVariant value;

if ( QgsAttributeEditor::retrieveValue( mProxyWidgets.value( idx ), mLayer, idx, value ) )
if ( QgsAttributeEditor::retrieveValue( mContext.proxyWidget( mLayer, idx ), mLayer, idx, value ) )
mFeature->setAttribute( idx, value );
}
}
Expand Down
32 changes: 26 additions & 6 deletions src/gui/qgsattributedialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,37 @@
#define QGSATTRIBUTEDIALOG_H

#include "qgsfeature.h"
#include "qgsattributeeditorcontext.h"

class QDialog;
class QgsFeature;
class QLayout;

class QgsDistanceArea;
class QgsFeature;
class QgsField;
class QgsVectorLayer;
class QgsHighlight;
class QgsDistanceArea;
class QgsVectorLayer;

class GUI_EXPORT QgsAttributeDialog : public QObject
{
Q_OBJECT

public:
/**
* @brief QgsAttributeDialog
* @param vl
* @param thepFeature
* @param featureOwner
* @param myDa
* @param parent
* @param showDialogButtons
*
* @deprecated
*/
QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QgsDistanceArea myDa, QWidget* parent = 0, bool showDialogButtons = true );

QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QWidget* parent = 0, bool showDialogButtons = true, QgsAttributeEditorContext context = QgsAttributeEditorContext() );

~QgsAttributeDialog();

/** Saves the size and position for the next time
Expand Down Expand Up @@ -63,18 +79,22 @@ class GUI_EXPORT QgsAttributeDialog : public QObject
bool eventFilter( QObject *obj, QEvent *event );

private:
void init();

QDialog *mDialog;
QString mSettingsPath;
// Used to sync multiple widgets for the same field
QMap<int, QWidget*> mProxyWidgets;
QgsAttributeEditorContext mContext;
QgsVectorLayer *mLayer;
QgsFeature *mFeature;
QgsFeature* mFeature;
bool mFeatureOwner;
QgsHighlight *mHighlight;
int mFormNr;
static int smFormCounter;
bool mShowDialogButtons;
QString mReturnvarname;

static int sFormCounter;
static QString sSettingsPath;
};

#endif
117 changes: 80 additions & 37 deletions src/gui/qgsattributeeditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,25 @@
* *
***************************************************************************/


#include "qgseditorwidgetfactory.h"
#include "qgseditorwidgetregistry.h"
#include "qgseditorwidgetwrapper.h"

#include "qgsattributedialog.h"
#include "qgsattributeeditorcontext.h"
#include "qgsattributeeditor.h"
#include <qgsvectorlayer.h>
#include <qgsvectordataprovider.h>
#include <qgscategorizedsymbolrendererv2.h>
#include <qgslonglongvalidator.h>
#include <qgsfieldvalidator.h>
#include <qgsmaplayerregistry.h>
#include <qgslogger.h>
#include <qgsexpression.h>
#include <qgsfilterlineedit.h>
#include <qgscolorbutton.h>
#include <qgsnetworkaccessmanager.h>
#include "qgscategorizedsymbolrendererv2.h"
#include "qgscolorbutton.h"
#include "qgsexpression.h"
#include "qgsfieldvalidator.h"
#include "qgsfilterlineedit.h"
#include "qgslogger.h"
#include "qgslonglongvalidator.h"
#include "qgsmaplayerregistry.h"
#include "qgsnetworkaccessmanager.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"

#include <QScrollArea>
#include <QPushButton>
Expand Down Expand Up @@ -265,11 +272,21 @@ QListWidget *QgsAttributeEditor::listWidget( QWidget *editor, QWidget *parent )

QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *editor, QgsVectorLayer *vl, int idx, const QVariant &value )
{
QMap<int, QWidget*> dummyProxyWidgets;
return createAttributeEditor( parent, editor, vl, idx, value, dummyProxyWidgets );
QgsAttributeEditorContext context;
context.addProxyWidgets( vl, QMap<int, QWidget*>() );

return createAttributeEditor( parent, editor, vl, idx, value, context );
}

QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *editor, QgsVectorLayer *vl, int idx, const QVariant &value, QMap<int, QWidget*> &proxyWidgets )
{
QgsAttributeEditorContext context;
context.addProxyWidgets( vl, proxyWidgets );

return createAttributeEditor( parent, editor, vl, idx, value, context );
}

QWidget* QgsAttributeEditor::createAttributeEditor( QWidget* parent, QWidget* editor, QgsVectorLayer* vl, int idx, const QVariant& value, QgsAttributeEditorContext& context )
{
if ( !vl )
return 0;
Expand Down Expand Up @@ -444,6 +461,18 @@ QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *ed
}
break;

case QgsVectorLayer::EditorWidgetV2:
{
const QString& widgetType = vl->editorWidgetV2( idx );
const QgsEditorWidgetConfig widgetConfig = vl->editorWidgetV2Config( idx );

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

if ( eww )
myWidget = eww->widget();
}
break;

case QgsVectorLayer::Classification:
{
QMap<QString, QString> classes;
Expand Down Expand Up @@ -658,11 +687,10 @@ QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *ed

QgsStringRelay* relay = NULL;

QMap<int, QWidget*>::const_iterator it = proxyWidgets.find( idx );
if ( it != proxyWidgets.end() )
QWidget* pwdg = context.proxyWidget( vl, idx );
if ( pwdg )
{
QObject* obj = qvariant_cast<QObject*>(( *it )->property( "QgisAttrEditProxy" ) );
relay = qobject_cast<QgsStringRelay*>( obj );
relay = pwdg->property( "QgisAttrEditProxy" ).value<QgsStringRelay*>();
}
else
{
Expand Down Expand Up @@ -697,7 +725,7 @@ QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *ed
if ( !cb || cb->isEditable() )
{
myWidget->setProperty( "QgisAttrEditSlot", QVariant( QByteArray( wSlot ) ) );
myWidget->setProperty( "QgisAttrEditProxy", QVariant( QMetaType::QObjectStar, &relay ) );
myWidget->setProperty( "QgisAttrEditProxy", QVariant::fromValue<QgsStringRelay*>( relay ) );
}
}
}
Expand Down Expand Up @@ -893,8 +921,7 @@ QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *ed
break;
}

QMap<int, QWidget*>::const_iterator it = proxyWidgets.find( idx );
if ( it != proxyWidgets.end() )
if ( context.proxyWidget( vl, idx ) )
{
if ( !synchronized )
{
Expand All @@ -903,7 +930,7 @@ QWidget *QgsAttributeEditor::createAttributeEditor( QWidget *parent, QWidget *ed
}
else
{
proxyWidgets.insert( idx, myWidget );
context.addProxyWidget( vl, idx, myWidget );
}

setValue( myWidget, vl, idx, value );
Expand All @@ -916,6 +943,14 @@ bool QgsAttributeEditor::retrieveValue( QWidget *widget, QgsVectorLayer *vl, int
if ( !widget )
return false;

QgsEditorWidgetWrapper* wrapper = QgsEditorWidgetWrapper::fromWidget( widget );

if ( wrapper )
{
value = wrapper->value();
return true;
}

const QgsField &theField = vl->pendingFields()[idx];
QgsVectorLayer::EditType editType = vl->editType( idx );
bool modified = false;
Expand Down Expand Up @@ -1332,12 +1367,20 @@ bool QgsAttributeEditor::setValue( QWidget *editor, QgsVectorLayer *vl, int idx,

case QgsVectorLayer::Hidden:
break;

case QgsVectorLayer::EditorWidgetV2:
QgsEditorWidgetWrapper* wrapper = QgsEditorWidgetWrapper::fromWidget( editor );
if ( wrapper )
{
wrapper->setValue( value );
}
break;
}

return true;
}

QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElement* widgetDef, QWidget* parent, QgsVectorLayer* vl, QgsAttributes &attrs, QMap<int, QWidget*> &proxyWidgets, bool createGroupBox )
QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElement* widgetDef, QWidget* parent, QgsVectorLayer* vl, const QgsFeature& feat, QgsAttributeEditorContext& context, QString& labelText, bool& labelOnTop )
{
QWidget *newWidget = 0;

Expand All @@ -1347,7 +1390,7 @@ QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElemen
{
const QgsAttributeEditorField* fieldDef = dynamic_cast<const QgsAttributeEditorField*>( widgetDef );
int fldIdx = fieldDef->idx();
newWidget = createAttributeEditor( parent, 0, vl, fldIdx, attrs.value( fldIdx, QVariant() ), proxyWidgets );
newWidget = createAttributeEditor( parent, 0, vl, fldIdx, feat.attributes().value( fldIdx, QVariant() ), context );

if ( newWidget )
{
Expand Down Expand Up @@ -1382,7 +1425,8 @@ QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElemen
}
}
}

labelOnTop = vl->labelOnTop( fieldDef->idx() );
labelText = vl->attributeDisplayName( fieldDef->idx() );
break;
}

Expand All @@ -1391,7 +1435,7 @@ QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElemen
const QgsAttributeEditorContainer* container = dynamic_cast<const QgsAttributeEditorContainer*>( widgetDef );
QWidget* myContainer;

if ( createGroupBox )
if ( container->isGroupBox() )
{
QGroupBox* groupBox = new QGroupBox( parent );
groupBox->setTitle( container->name() );
Expand All @@ -1418,26 +1462,23 @@ QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElemen

QList<QgsAttributeEditorElement*> children = container->children();

for ( QList<QgsAttributeEditorElement*>::const_iterator it = children.begin(); it != children.end(); ++it )
Q_FOREACH( QgsAttributeEditorElement* childDef, children )
{
QgsAttributeEditorElement* childDef = *it;
QWidget* editor = createWidgetFromDef( childDef, myContainer, vl, attrs, proxyWidgets, true );
QString labelText;
bool labelOnTop;
QWidget* editor = createWidgetFromDef( childDef, myContainer, vl, feat, context, labelText, labelOnTop );

if ( childDef->type() == QgsAttributeEditorElement::AeTypeContainer )
if ( labelText == QString::null )
{
gbLayout->addWidget( editor, index, 0, 1, 2 );
}
else
{
const QgsAttributeEditorField* fieldDef = dynamic_cast<const QgsAttributeEditorField*>( childDef );

//show attribute alias if available
QString myFieldName = vl->attributeDisplayName( fieldDef->idx() );
QLabel *mypLabel = new QLabel( myFieldName, myContainer );

if ( vl->labelOnTop( fieldDef->idx() ) )
QLabel* mypLabel = new QLabel( labelText );
if ( labelOnTop )
{
gbLayout->addWidget( mypLabel, index++, 0, 1, 2 );
gbLayout->addWidget( mypLabel, index, 0, 1, 2 );
++index;
gbLayout->addWidget( editor, index, 0, 1 , 2 );
}
else
Expand All @@ -1451,6 +1492,8 @@ QWidget* QgsAttributeEditor::createWidgetFromDef( const QgsAttributeEditorElemen
}
gbLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ), index , 0 );

labelText = QString::null;
labelOnTop = true;
break;
}

Expand Down
44 changes: 35 additions & 9 deletions src/gui/qgsattributeeditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,31 @@

#include <QVariant>
#include <QMetaType>
#include <QGridLayout>

#include "qgsfeature.h"

class QObject;
class QWidget;
class QgsVectorLayer;
class QComboBox;
class QListWidget;

class QgsAttributeEditorContext;
class QgsAttributeEditorElement;
class QgsDualView;
class QgsVectorLayer;

/* \brief create attribute widget for editing */
class GUI_EXPORT QgsAttributeEditor : public QObject
{
Q_OBJECT

public:
QgsAttributeEditor( QObject *parent, QgsVectorLayer *vl = 0, int idx = -1 )
QgsAttributeEditor( QObject* parent, QgsVectorLayer* vl = 0, int idx = -1 )
: QObject( parent )
, mLayer( vl )
, mIdx( idx )
{};
{}
/**
* Creates or prepares a attributre editor widget
* @param parent The parent object
Expand All @@ -49,8 +53,10 @@ class GUI_EXPORT QgsAttributeEditor : public QObject
* @param value the value to initiate this widget with
* @param proxyWidgets an array of widgets, which will act as a value proxy if the same field is inserted multiple times
*
* @deprecated
*/
static QWidget *createAttributeEditor( QWidget *parent, QWidget *editor, QgsVectorLayer *vl, int idx, const QVariant &value, QMap<int, QWidget*> &proxyWidgets );
static QWidget* createAttributeEditor( QWidget* parent, QWidget* editor, QgsVectorLayer* vl, int idx, const QVariant &value, QMap<int, QWidget*>& proxyWidgets );

/**
* Creates or prepares a attributre editor widget
* @param parent The parent object
Expand All @@ -60,24 +66,42 @@ class GUI_EXPORT QgsAttributeEditor : public QObject
* @param value the value to initiate this widget with
*
*/
static QWidget *createAttributeEditor( QWidget *parent, QWidget *editor, QgsVectorLayer *vl, int idx, const QVariant &value );
static QWidget* createAttributeEditor( QWidget* parent, QWidget* editor, QgsVectorLayer* vl, int idx, const QVariant& value );

/**
* Creates or prepares a attributre editor widget
* @param parent The parent object
* @param editor The widget to prepare. Set to null if it should be generated
* @param vl The vector layer to use as data source
* @param idx The field index this widget refers to
* @param value the value to initiate this widget with
* @param context the context used for the created attribute editor
*
*/
static QWidget* createAttributeEditor( QWidget* parent, QWidget* editor, QgsVectorLayer* vl, int idx, const QVariant& value, QgsAttributeEditorContext& context );

/**
* Creates a widget form a QgsAttributeEditorElement definition. Will recursively generate containers and widgets.
* @param widgetDef The definition for the widget
* @param parent The parent object
* @param vl The vector layer to use as data source
* @param attrs Attributes for the current feature.
* @param proxyWidgets An array of widgets, which will act as a value proxy if the same field is inserted multiple times
* @param createGroupBox If the element is a container, should a GroupBox be created to hold the children?
* @param feat The feature to create the widget for
* @param context the context used for the created attribute editor
* @param [out] labelText An optional label text will be written into the referenced QString. It will be set to
* a QString::null value if no label should be shown
* @param [out] labelOnTop Will be set to true if the label should be placed on top of the field.
* If set to false, the label should be shown left or right of the field
*
*/
static QWidget *createWidgetFromDef( const QgsAttributeEditorElement* widgetDef, QWidget* parent, QgsVectorLayer* vl, QgsAttributes &attrs, QMap<int, QWidget*> &proxyWidgets, bool createGroupBox );
static QWidget *createWidgetFromDef( const QgsAttributeEditorElement* widgetDef, QWidget* parent, QgsVectorLayer* vl, const QgsFeature &feat, QgsAttributeEditorContext& context, QString& labelText, bool& labelOnTop );

static bool retrieveValue( QWidget *widget, QgsVectorLayer *vl, int idx, QVariant &value );
static bool setValue( QWidget *widget, QgsVectorLayer *vl, int idx, const QVariant &value );

private:
static QComboBox *comboBox( QWidget *editor, QWidget *parent );
static QListWidget *listWidget( QWidget *editor, QWidget *parent );
static QgsDualView* dualView( QWidget* editor, QWidget* parent );

public slots:
void selectFileName();
Expand Down Expand Up @@ -115,4 +139,6 @@ class GUI_EXPORT QgsStringRelay : public QObject
QList<QWidget*> mProxyList;
};

Q_DECLARE_METATYPE( QgsStringRelay* )

#endif
42 changes: 42 additions & 0 deletions src/gui/qgsattributeeditorcontext.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/***************************************************************************
qgsattributeeditorcontext.cpp
--------------------------------------
Date : 30.7.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 "qgsattributeeditorcontext.h"

QgsAttributeEditorContext::QgsAttributeEditorContext()
{

}

QWidget*QgsAttributeEditorContext::proxyWidget( QgsVectorLayer* vl, int fieldIdx )
{
return mProxyWidgets.value( vl ).value( fieldIdx );
}

void QgsAttributeEditorContext::addProxyWidgets( QgsVectorLayer* vl, QMap<int, QWidget*> proxyWidgets )
{
mProxyWidgets[ vl ].unite( proxyWidgets );
}

void QgsAttributeEditorContext::addProxyWidget( QgsVectorLayer* vl, int idx, QWidget* widget )
{
mProxyWidgets[ vl ].insert( idx, widget );
}


void QgsAttributeEditorContext::adjustForLayer( QgsVectorLayer* layer )
{
mDistanceArea.setSourceCrs( layer->crs() );
}
59 changes: 59 additions & 0 deletions src/gui/qgsattributeeditorcontext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/***************************************************************************
qgsattributeeditorcontext.h
--------------------------------------
Date : 30.7.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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 QGSATTRIBUTEEDITORCONTEXT_H
#define QGSATTRIBUTEEDITORCONTEXT_H

#include <QMap>
#include <QWidget>

#include <qgsdistancearea.h>
#include <qgsvectorlayer.h>


/**
* This class contains context information for attribute editor widgets.
* It will be passed to embedded widgets whenever this occurs (e.g. when
* showing an embedded form due to relations)
*/

class GUI_EXPORT QgsAttributeEditorContext
{
public:
QgsAttributeEditorContext();

QWidget* proxyWidget( QgsVectorLayer* vl, int fieldIdx );
void addProxyWidgets( QgsVectorLayer* vl, QMap<int, QWidget*> proxyWidgets );
void addProxyWidget( QgsVectorLayer* vl, int idx, QWidget* widget );

void setDistanceArea( const QgsDistanceArea& distanceArea ) { mDistanceArea = distanceArea; }
inline const QgsDistanceArea& distanceArea() { return mDistanceArea; }

/**
* When copying the context for another layer, call this.
* Will adjast the distance area for this layer
*
* @param layer The layer to adjust for.
*/
void adjustForLayer( QgsVectorLayer* layer );

private:
//! vectorlayer => ( fieldIdx, proxyWidget )
QMap<QgsVectorLayer*, QMap<int, QWidget*> > mProxyWidgets;

QgsDistanceArea mDistanceArea;
};

#endif // QGSATTRIBUTEEDITORCONTEXT_H
25 changes: 14 additions & 11 deletions src/ui/qgsattributetypeedit.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,13 @@
<x>0</x>
<y>0</y>
<width>751</width>
<height>487</height>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
<string>Attribute Edit Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="5">
<widget class="QListWidget" name="selectionListWidget">
<item>
Expand Down Expand Up @@ -123,6 +113,16 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QStackedWidget" name="stackedWidget">
<property name="sizePolicy">
Expand Down Expand Up @@ -878,6 +878,9 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="pageEditorWidget">
<layout class="QHBoxLayout" name="horizontalLayout"/>
</widget>
</widget>
</item>
<item row="0" column="1">
Expand Down
Loading