QGIS API Documentation  2.11.0-Master
qgseditorwidgetregistry.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgseditorwidgetregistry.cpp
3  --------------------------------------
4  Date : 24.4.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
19 #include "qgslegacyhelpers.h"
20 #include "qgsmessagelog.h"
21 #include "qgsproject.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsmaplayerregistry.h"
24 
25 // Editors
27 #include "qgsrangewidgetfactory.h"
32 #include "qgshiddenwidgetfactory.h"
36 #include "qgsuuidwidgetfactory.h"
37 #include "qgsphotowidgetfactory.h"
38 #ifdef WITH_QTWEBKIT
40 #endif
41 #include "qgscolorwidgetfactory.h"
43 #include "qgsdatetimeeditfactory.h"
44 
46 {
47  static QgsEditorWidgetRegistry sInstance;
48  return &sInstance;
49 }
50 
52 {
54  reg->registerWidget( "Classification", new QgsClassificationWidgetWrapperFactory( tr( "Classification" ) ) );
55  reg->registerWidget( "Range", new QgsRangeWidgetFactory( tr( "Range" ) ) );
56  reg->registerWidget( "UniqueValues", new QgsUniqueValueWidgetFactory( tr( "Unique Values" ) ) );
57  reg->registerWidget( "FileName", new QgsFileNameWidgetFactory( tr( "File Name" ) ) );
58  reg->registerWidget( "ValueMap", new QgsValueMapWidgetFactory( tr( "Value Map" ) ) );
59  reg->registerWidget( "Enumeration", new QgsEnumerationWidgetFactory( tr( "Enumeration" ) ) );
60  reg->registerWidget( "Hidden", new QgsHiddenWidgetFactory( tr( "Hidden" ) ) );
61  reg->registerWidget( "CheckBox", new QgsCheckboxWidgetFactory( tr( "Check Box" ) ) );
62  reg->registerWidget( "TextEdit", new QgsTextEditWidgetFactory( tr( "Text Edit" ) ) );
63  reg->registerWidget( "ValueRelation", new QgsValueRelationWidgetFactory( tr( "Value Relation" ) ) );
64  reg->registerWidget( "UuidGenerator", new QgsUuidWidgetFactory( tr( "Uuid Generator" ) ) );
65  reg->registerWidget( "Photo", new QgsPhotoWidgetFactory( tr( "Photo" ) ) );
66 #ifdef WITH_QTWEBKIT
67  reg->registerWidget( "WebView", new QgsWebViewWidgetFactory( tr( "Web View" ) ) );
68 #endif
69  reg->registerWidget( "Color", new QgsColorWidgetFactory( tr( "Color" ) ) );
70  reg->registerWidget( "RelationReference", new QgsRelationReferenceFactory( tr( "Relation Reference" ), mapCanvas, messageBar ) );
71  reg->registerWidget( "DateTime", new QgsDateTimeEditFactory( tr( "Date/Time" ) ) );
72 }
73 
75 {
76  connect( QgsProject::instance(), SIGNAL( readMapLayer( QgsMapLayer*, const QDomElement& ) ), this, SLOT( readMapLayer( QgsMapLayer*, const QDomElement& ) ) );
77  connect( QgsProject::instance(), SIGNAL( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ), this, SLOT( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ) );
78 
79  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( mapLayerAdded( QgsMapLayer* ) ) );
80 }
81 
83 {
84  qDeleteAll( mWidgetFactories.values() );
85 }
86 
87 QgsEditorWidgetWrapper* QgsEditorWidgetRegistry::create( const QString& widgetId, QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, QWidget* editor, QWidget* parent, const QgsAttributeEditorContext &context )
88 {
89  if ( mWidgetFactories.contains( widgetId ) )
90  {
91  QgsEditorWidgetWrapper* ww = mWidgetFactories[widgetId]->create( vl, fieldIdx, editor, parent );
92 
93  if ( ww )
94  {
95  ww->setConfig( config );
96  ww->setContext( context );
97  // Make sure that there is a widget created at this point
98  // so setValue() et al won't crash
99  ww->widget();
100 
101  // If we tried to set a widget which is not supported by this wrapper
102  if ( !ww->valid() )
103  {
104  delete ww;
105  QString wid = findSuitableWrapper( editor, "TextEdit" );
106  ww = mWidgetFactories[wid]->create( vl, fieldIdx, editor, parent );
107  ww->setConfig( config );
108  ww->setContext( context );
109  }
110 
111  return ww;
112  }
113  }
114 
115  return 0;
116 }
117 
119 {
120  if ( mWidgetFactories.contains( widgetId ) )
121  {
122  QgsSearchWidgetWrapper* ww = mWidgetFactories[widgetId]->createSearchWidget( vl, fieldIdx, parent );
123 
124  if ( ww )
125  {
126  ww->setConfig( config );
127  ww->setContext( context );
128  // Make sure that there is a widget created at this point
129  // so setValue() et al won't crash
130  ww->widget();
131  return ww;
132  }
133  }
134  return 0;
135 }
136 
138 {
139  if ( mWidgetFactories.contains( widgetId ) )
140  {
141  return mWidgetFactories[widgetId]->configWidget( vl, fieldIdx, parent );
142  }
143  return 0;
144 }
145 
147 {
148  if ( mWidgetFactories.contains( widgetId ) )
149  {
150  return mWidgetFactories[widgetId]->name();
151  }
152 
153  return QString();
154 }
155 
157 {
158  return mWidgetFactories;
159 }
160 
162 {
163  return mWidgetFactories.value( widgetId );
164 }
165 
167 {
168  if ( !widgetFactory )
169  {
170  QgsMessageLog::instance()->logMessage( "QgsEditorWidgetRegistry: Factory not valid." );
171  return false;
172  }
173  else if ( mWidgetFactories.contains( widgetId ) )
174  {
175  QgsMessageLog::instance()->logMessage( QString( "QgsEditorWidgetRegistry: Factory with id %1 already registered." ).arg( widgetId ) );
176  return false;
177  }
178  else
179  {
180  mWidgetFactories.insert( widgetId, widgetFactory );
181 
182  // Use this factory as default where it provides the heighest priority
183  QMap<const char*, int> types = widgetFactory->supportedWidgetTypes();
185  it = types.constBegin();
186 
187  for ( ; it != types.constEnd(); ++it )
188  {
189  if ( it.value() > mFactoriesByType[it.key()].first )
190  {
191  mFactoriesByType[it.key()] = qMakePair( it.value(), widgetId );
192  }
193  }
194 
195  return true;
196  }
197 }
198 
199 void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomElement& layerElem )
200 {
201  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
202  {
203  return;
204  }
205 
206  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
207  Q_ASSERT( vectorLayer );
208 
209  QDomNodeList editTypeNodes = layerElem.namedItem( "edittypes" ).childNodes();
210 
211  for ( int i = 0; i < editTypeNodes.size(); i++ )
212  {
213  QDomNode editTypeNode = editTypeNodes.at( i );
214  QDomElement editTypeElement = editTypeNode.toElement();
215 
216  QString name = editTypeElement.attribute( "name" );
217 
218  int idx = vectorLayer->fieldNameIndex( name );
219  if ( idx == -1 )
220  continue;
221 
222  bool hasLegacyType;
223  QgsVectorLayer::EditType editType =
224  ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt( &hasLegacyType );
225 
226  QString ewv2Type;
228 
229  if ( hasLegacyType && editType != QgsVectorLayer::EditorWidgetV2 )
230  {
232  ewv2Type = readLegacyConfig( vectorLayer, editTypeElement, cfg );
234  }
235  else
236  ewv2Type = editTypeElement.attribute( "widgetv2type" );
237 
238  if ( mWidgetFactories.contains( ewv2Type ) )
239  {
240  vectorLayer->setEditorWidgetV2( idx, ewv2Type );
241  QDomElement ewv2CfgElem = editTypeElement.namedItem( "widgetv2config" ).toElement();
242 
243  if ( !ewv2CfgElem.isNull() )
244  {
245  cfg = mWidgetFactories[ewv2Type]->readEditorConfig( ewv2CfgElem, vectorLayer, idx );
246  }
247 
248  vectorLayer->setFieldEditable( idx, ewv2CfgElem.attribute( "fieldEditable", "1" ) == "1" );
249  vectorLayer->setLabelOnTop( idx, ewv2CfgElem.attribute( "labelOnTop", "0" ) == "1" );
250  vectorLayer->setEditorWidgetV2Config( idx, cfg );
251  }
252  else
253  {
254  QgsMessageLog::logMessage( tr( "Unknown attribute editor widget '%1'" ).arg( ewv2Type ) );
255  }
256  }
257 }
258 
259 const QString QgsEditorWidgetRegistry::readLegacyConfig( QgsVectorLayer* vl, const QDomElement& editTypeElement, QgsEditorWidgetConfig& cfg )
260 {
261  QString name = editTypeElement.attribute( "name" );
262 
263  QgsVectorLayer::EditType editType = ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt();
264 
266  return QgsLegacyHelpers::convertEditType( editType, cfg, vl, name, editTypeElement );
268 }
269 
270 void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement& layerElem, QDomDocument& doc ) const
271 {
272  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
273  {
274  return;
275  }
276 
277  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
278  if ( !vectorLayer )
279  {
280  return;
281  }
282 
283  QDomNode editTypesNode = doc.createElement( "edittypes" );
284 
285  QgsFields fields = vectorLayer->fields();
286  for ( int idx = 0; idx < fields.count(); ++idx )
287  {
288  const QgsField &field = fields[ idx ];
289  const QString& widgetType = vectorLayer->editorWidgetV2( idx );
290  if ( !mWidgetFactories.contains( widgetType ) )
291  {
292  QgsMessageLog::logMessage( tr( "Could not save unknown editor widget type '%1'." ).arg( widgetType ) );
293  continue;
294  }
295 
296 
297  QDomElement editTypeElement = doc.createElement( "edittype" );
298  editTypeElement.setAttribute( "name", field.name() );
299  editTypeElement.setAttribute( "widgetv2type", widgetType );
300 
301  if ( mWidgetFactories.contains( widgetType ) )
302  {
303  QDomElement ewv2CfgElem = doc.createElement( "widgetv2config" );
304  ewv2CfgElem.setAttribute( "fieldEditable", vectorLayer->fieldEditable( idx ) );
305  ewv2CfgElem.setAttribute( "labelOnTop", vectorLayer->labelOnTop( idx ) );
306 
307  mWidgetFactories[widgetType]->writeConfig( vectorLayer->editorWidgetV2Config( idx ), ewv2CfgElem, doc, vectorLayer, idx );
308 
309  editTypeElement.appendChild( ewv2CfgElem );
310  }
311 
312  editTypesNode.appendChild( editTypeElement );
313  }
314 
315  layerElem.appendChild( editTypesNode );
316 }
317 
318 void QgsEditorWidgetRegistry::mapLayerAdded( QgsMapLayer* mapLayer )
319 {
320  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
321 
322  if ( vl )
323  {
324  connect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
325  connect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
326  }
327 }
328 
329 void QgsEditorWidgetRegistry::readSymbology( const QDomElement& element, QString& errorMessage )
330 {
331  Q_UNUSED( errorMessage )
332  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
333 
334  Q_ASSERT( vl );
335 
336  readMapLayer( vl, element );
337 }
338 
339 void QgsEditorWidgetRegistry::writeSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage )
340 {
341  Q_UNUSED( errorMessage )
342  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
343 
344  Q_ASSERT( vl );
345 
346  writeMapLayer( vl, element, doc );
347 }
348 
349 QString QgsEditorWidgetRegistry::findSuitableWrapper( QWidget* editor, const QString& defaultWidget )
350 {
351  QMap<const char*, QPair<int, QString> >::ConstIterator it;
352 
353  QString widgetid;
354  int weight = 0;
355 
356  it = mFactoriesByType.constBegin();
357  for ( ; it != mFactoriesByType.constEnd(); ++it )
358  {
359  if ( editor->staticMetaObject.className() == it.key() )
360  {
361  // if it's a perfect match: return it directly
362  return it.value().second;
363  }
364  else if ( editor->inherits( it.key() ) )
365  {
366  // if it's a subclass, continue evaluating, maybe we find a more-specific or one with more weight
367  if ( it.value().first > weight )
368  {
369  weight = it.value().first;
370  widgetid = it.value().second;
371  }
372  }
373  }
374 
375  if ( widgetid.isNull() )
376  widgetid = defaultWidget;
377  return widgetid;
378 }
const QgsEditorWidgetConfig editorWidgetV2Config(int fieldIdx) const
Get the configuration for the editor widget used to represent the field at the given index...
virtual bool valid()=0
Return true if the widget has been properly initialized.
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:70
Manages an editor widget Widget and wrapper share the same parent.
bool fieldEditable(int idx)
Is edit widget editable.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QgsMapLayer::LayerType type() const
Get the type of the layer.
Definition: qgsmaplayer.cpp:93
virtual QMap< const char *, int > supportedWidgetTypes()
Returns a list of widget types which this editor widget supports.
bool contains(const Key &key) const
This class should be subclassed for every configurable editor widget type.
QList< T > values() const
QDomNode appendChild(const QDomNode &newChild)
QgsEditorWidgetFactory * factory(const QString &widgetId)
Get a factory for the given widget type id.
QString attribute(const QString &name, const QString &defValue) const
This class manages all known edit widget factories.
QgsFields fields() const
Returns the list of fields of this layer.
This class contains context information for attribute editor widgets.
QObject * sender() const
Manages an editor widget Widget and wrapper share the same parent.
const_iterator constBegin() const
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:464
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:42
Container of fields for a vector layer.
Definition: qgsfield.h:177
QgsEditorConfigWidget * createConfigWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, QWidget *parent)
Creates a configuration widget.
QDomNodeList childNodes() const
static QgsEditorWidgetRegistry * instance()
This class is a singleton and has therefore to be accessed with this method instead of a constructor...
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:105
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
bool isNull() const
QDomElement toElement() const
void setContext(const QgsAttributeEditorContext &context)
Set the context in which this widget is shown.
const char * name() const
void setConfig(const QgsEditorWidgetConfig &config)
Will set the config of this wrapper to the specified config.
void setAttribute(const QString &name, const QString &value)
QgsEditorWidgetWrapper * create(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, QWidget *editor, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Create an attribute editor widget wrapper of a given type for a given field.
const QString editorWidgetV2(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
int toInt(bool *ok, int base) const
const QMap< QString, QgsEditorWidgetFactory * > & factories()
Get access to all registered factories.
const_iterator constEnd() const
Every attribute editor widget needs a factory, which inherits this class.
int count() const
Return number of items.
Definition: qgsfield.cpp:285
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:40
bool labelOnTop(int idx)
Label widget on top.
QDomNode namedItem(const QString &name) const
void setEditorWidgetV2Config(int attrIdx, const QgsEditorWidgetConfig &config)
Set the editor widget config for a field.
bool isNull() const
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:465
const Key key(const T &value) const
void setEditorWidgetV2(int attrIdx, const QString &widgetType)
Set the editor widget type for a field.
modularized edit widgets
QgsSearchWidgetWrapper * createSearchWidget(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
static Q_DECL_DEPRECATED const QString convertEditType(QgsVectorLayer::EditType editType, QgsEditorWidgetConfig &cfg, QgsVectorLayer *vl, const QString &name, const QDomElement &editTypeElement=QDomElement())
static QgsMessageLog * instance()
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:352
void setFieldEditable(int idx, bool editable)
Set edit widget editable.
iterator insert(const Key &key, const T &value)
QWidget * widget()
Access the widget managed by this wrapper.
static void initEditors(QgsMapCanvas *mapCanvas=0, QgsMessageBar *messageBar=0)
Registers all the default widgets.
void setLabelOnTop(int idx, bool onTop)
Label widget on top.
bool registerWidget(const QString &widgetId, QgsEditorWidgetFactory *widgetFactory)
Register a new widget factory with the given id.
int size() const
QDomElement createElement(const QString &tagName)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
QDomNode at(int index) const
const T value(const Key &key) const