QGIS API Documentation  2.15.0-Master (94d88e6)
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
28 #include "qgscolorwidgetfactory.h"
29 #include "qgsdatetimeeditfactory.h"
33 #include "qgshiddenwidgetfactory.h"
34 #include "qgsphotowidgetfactory.h"
35 #include "qgsrangewidgetfactory.h"
39 #include "qgsuuidwidgetfactory.h"
42 #ifdef WITH_QTWEBKIT
44 #endif
45 
46 
48 {
49  static QgsEditorWidgetRegistry sInstance;
50  return &sInstance;
51 }
52 
54 {
56  reg->registerWidget( "Classification", new QgsClassificationWidgetWrapperFactory( tr( "Classification" ) ) );
57  reg->registerWidget( "Range", new QgsRangeWidgetFactory( tr( "Range" ) ) );
58  reg->registerWidget( "UniqueValues", new QgsUniqueValueWidgetFactory( tr( "Unique Values" ) ) );
59  reg->registerWidget( "FileName", new QgsFileNameWidgetFactory( tr( "File Name" ) ) );
60  reg->registerWidget( "ValueMap", new QgsValueMapWidgetFactory( tr( "Value Map" ) ) );
61  reg->registerWidget( "Enumeration", new QgsEnumerationWidgetFactory( tr( "Enumeration" ) ) );
62  reg->registerWidget( "Hidden", new QgsHiddenWidgetFactory( tr( "Hidden" ) ) );
63  reg->registerWidget( "CheckBox", new QgsCheckboxWidgetFactory( tr( "Check Box" ) ) );
64  reg->registerWidget( "TextEdit", new QgsTextEditWidgetFactory( tr( "Text Edit" ) ) );
65  reg->registerWidget( "ValueRelation", new QgsValueRelationWidgetFactory( tr( "Value Relation" ) ) );
66  reg->registerWidget( "UuidGenerator", new QgsUuidWidgetFactory( tr( "Uuid Generator" ) ) );
67  reg->registerWidget( "Photo", new QgsPhotoWidgetFactory( tr( "Photo" ) ) );
68 #ifdef WITH_QTWEBKIT
69  reg->registerWidget( "WebView", new QgsWebViewWidgetFactory( tr( "Web View" ) ) );
70 #endif
71  reg->registerWidget( "Color", new QgsColorWidgetFactory( tr( "Color" ) ) );
72  reg->registerWidget( "RelationReference", new QgsRelationReferenceFactory( tr( "Relation Reference" ), mapCanvas, messageBar ) );
73  reg->registerWidget( "DateTime", new QgsDateTimeEditFactory( tr( "Date/Time" ) ) );
74  reg->registerWidget( "ExternalResource", new QgsExternalResourceWidgetFactory( tr( "External Resource" ) ) );
75 }
76 
78 {
79  connect( QgsProject::instance(), SIGNAL( readMapLayer( QgsMapLayer*, const QDomElement& ) ), this, SLOT( readMapLayer( QgsMapLayer*, const QDomElement& ) ) );
80  // connect( QgsProject::instance(), SIGNAL( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ), this, SLOT( writeMapLayer( QgsMapLayer*, QDomElement&, QDomDocument& ) ) );
81 
82  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( mapLayerAdded( QgsMapLayer* ) ) );
83 }
84 
86 {
87  qDeleteAll( mWidgetFactories );
88 }
89 
91 {
92  if ( mWidgetFactories.contains( widgetId ) )
93  {
94  QgsEditorWidgetWrapper* ww = mWidgetFactories[widgetId]->create( vl, fieldIdx, editor, parent );
95 
96  if ( ww )
97  {
98  ww->setConfig( config );
99  ww->setContext( context );
100  // Make sure that there is a widget created at this point
101  // so setValue() et al won't crash
102  ww->widget();
103 
104  // If we tried to set a widget which is not supported by this wrapper
105  if ( !ww->valid() )
106  {
107  delete ww;
108  QString wid = findSuitableWrapper( editor, "TextEdit" );
109  ww = mWidgetFactories[wid]->create( vl, fieldIdx, editor, parent );
110  ww->setConfig( config );
111  ww->setContext( context );
112  }
113 
114  return ww;
115  }
116  }
117 
118  return nullptr;
119 }
120 
122 {
123  if ( mWidgetFactories.contains( widgetId ) )
124  {
125  QgsSearchWidgetWrapper* ww = mWidgetFactories[widgetId]->createSearchWidget( vl, fieldIdx, parent );
126 
127  if ( ww )
128  {
129  ww->setConfig( config );
130  ww->setContext( context );
131  // Make sure that there is a widget created at this point
132  // so setValue() et al won't crash
133  ww->widget();
134  ww->clearWidget();
135  return ww;
136  }
137  }
138  return nullptr;
139 }
140 
142 {
143  if ( mWidgetFactories.contains( widgetId ) )
144  {
145  return mWidgetFactories[widgetId]->configWidget( vl, fieldIdx, parent );
146  }
147  return nullptr;
148 }
149 
151 {
152  if ( mWidgetFactories.contains( widgetId ) )
153  {
154  return mWidgetFactories[widgetId]->name();
155  }
156 
157  return QString();
158 }
159 
161 {
162  return mWidgetFactories;
163 }
164 
166 {
167  return mWidgetFactories.value( widgetId );
168 }
169 
171 {
172  if ( !widgetFactory )
173  {
174  QgsMessageLog::instance()->logMessage( "QgsEditorWidgetRegistry: Factory not valid." );
175  return false;
176  }
177  else if ( mWidgetFactories.contains( widgetId ) )
178  {
179  QgsMessageLog::instance()->logMessage( QString( "QgsEditorWidgetRegistry: Factory with id %1 already registered." ).arg( widgetId ) );
180  return false;
181  }
182  else
183  {
184  mWidgetFactories.insert( widgetId, widgetFactory );
185 
186  // Use this factory as default where it provides the heighest priority
187  QMap<const char*, int> types = widgetFactory->supportedWidgetTypes();
189  it = types.constBegin();
190 
191  for ( ; it != types.constEnd(); ++it )
192  {
193  if ( it.value() > mFactoriesByType[it.key()].first )
194  {
195  mFactoriesByType[it.key()] = qMakePair( it.value(), widgetId );
196  }
197  }
198 
199  return true;
200  }
201 }
202 
203 void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomElement& layerElem )
204 {
205  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
206  {
207  return;
208  }
209 
210  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
211  Q_ASSERT( vectorLayer );
212 
213  QDomNodeList editTypeNodes = layerElem.namedItem( "edittypes" ).childNodes();
214 
215  for ( int i = 0; i < editTypeNodes.size(); i++ )
216  {
217  QDomNode editTypeNode = editTypeNodes.at( i );
218  QDomElement editTypeElement = editTypeNode.toElement();
219 
220  QString name = editTypeElement.attribute( "name" );
221 
222  int idx = vectorLayer->fieldNameIndex( name );
223  if ( idx == -1 )
224  continue;
225 
226  bool hasLegacyType;
227  QgsVectorLayer::EditType editType =
228  ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt( &hasLegacyType );
229 
230  QString ewv2Type;
232 
233  if ( hasLegacyType && editType != QgsVectorLayer::EditorWidgetV2 )
234  {
236  ewv2Type = readLegacyConfig( vectorLayer, editTypeElement, cfg );
238  }
239  else
240  ewv2Type = editTypeElement.attribute( "widgetv2type" );
241 
242  if ( mWidgetFactories.contains( ewv2Type ) )
243  {
244  vectorLayer->editFormConfig()->setWidgetType( idx, ewv2Type );
245  QDomElement ewv2CfgElem = editTypeElement.namedItem( "widgetv2config" ).toElement();
246 
247  if ( !ewv2CfgElem.isNull() )
248  {
249  cfg = mWidgetFactories[ewv2Type]->readEditorConfig( ewv2CfgElem, vectorLayer, idx );
250  }
251 
252  vectorLayer->editFormConfig()->setReadOnly( idx, ewv2CfgElem.attribute( "fieldEditable", "1" ) != "1" );
253  vectorLayer->editFormConfig()->setLabelOnTop( idx, ewv2CfgElem.attribute( "labelOnTop", "0" ) == "1" );
254  vectorLayer->editFormConfig()->setWidgetConfig( idx, cfg );
255  }
256  else
257  {
258  QgsMessageLog::logMessage( tr( "Unknown attribute editor widget '%1'" ).arg( ewv2Type ) );
259  }
260  }
261 }
262 
263 const QString QgsEditorWidgetRegistry::readLegacyConfig( QgsVectorLayer* vl, const QDomElement& editTypeElement, QgsEditorWidgetConfig& cfg )
264 {
265  QString name = editTypeElement.attribute( "name" );
266 
267  QgsVectorLayer::EditType editType = ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt();
268 
270  return QgsLegacyHelpers::convertEditType( editType, cfg, vl, name, editTypeElement );
272 }
273 
274 void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement& layerElem, QDomDocument& doc ) const
275 {
276  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
277  {
278  return;
279  }
280 
281  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
282  if ( !vectorLayer )
283  {
284  return;
285  }
286 
287  QDomNode editTypesNode = doc.createElement( "edittypes" );
288 
289  QgsFields fields = vectorLayer->fields();
290  for ( int idx = 0; idx < fields.count(); ++idx )
291  {
292  const QgsField &field = fields.at( idx );
293  const QString& widgetType = vectorLayer->editFormConfig()->widgetType( idx );
294  if ( !mWidgetFactories.contains( widgetType ) )
295  {
296  QgsMessageLog::logMessage( tr( "Could not save unknown editor widget type '%1'." ).arg( widgetType ) );
297  continue;
298  }
299 
300 
301  QDomElement editTypeElement = doc.createElement( "edittype" );
302  editTypeElement.setAttribute( "name", field.name() );
303  editTypeElement.setAttribute( "widgetv2type", widgetType );
304 
305  if ( mWidgetFactories.contains( widgetType ) )
306  {
307  QDomElement ewv2CfgElem = doc.createElement( "widgetv2config" );
308  ewv2CfgElem.setAttribute( "fieldEditable", !vectorLayer->editFormConfig()->readOnly( idx ) );
309  ewv2CfgElem.setAttribute( "labelOnTop", vectorLayer->editFormConfig()->labelOnTop( idx ) );
310 
311  mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig()->widgetConfig( idx ), ewv2CfgElem, doc, vectorLayer, idx );
312 
313  editTypeElement.appendChild( ewv2CfgElem );
314  }
315 
316  editTypesNode.appendChild( editTypeElement );
317  }
318 
319  layerElem.appendChild( editTypesNode );
320 }
321 
322 void QgsEditorWidgetRegistry::mapLayerAdded( QgsMapLayer* mapLayer )
323 {
324  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
325 
326  if ( vl )
327  {
328  connect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
329  connect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
330  }
331 }
332 
333 void QgsEditorWidgetRegistry::readSymbology( const QDomElement& element, QString& errorMessage )
334 {
335  Q_UNUSED( errorMessage )
336  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
337 
338  Q_ASSERT( vl );
339 
340  readMapLayer( vl, element );
341 }
342 
343 void QgsEditorWidgetRegistry::writeSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage )
344 {
345  Q_UNUSED( errorMessage )
346  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
347 
348  Q_ASSERT( vl );
349 
350  writeMapLayer( vl, element, doc );
351 }
352 
353 QString QgsEditorWidgetRegistry::findSuitableWrapper( QWidget* editor, const QString& defaultWidget )
354 {
355  QMap<const char*, QPair<int, QString> >::ConstIterator it;
356 
357  QString widgetid;
358 
359  // Editor can be null
360  if ( editor )
361  {
362  int weight = 0;
363 
364  it = mFactoriesByType.constBegin();
365  for ( ; it != mFactoriesByType.constEnd(); ++it )
366  {
367  if ( editor->staticMetaObject.className() == it.key() )
368  {
369  // if it's a perfect match: return it directly
370  return it.value().second;
371  }
372  else if ( editor->inherits( it.key() ) )
373  {
374  // if it's a subclass, continue evaluating, maybe we find a more-specific or one with more weight
375  if ( it.value().first > weight )
376  {
377  weight = it.value().first;
378  widgetid = it.value().second;
379  }
380  }
381  }
382  }
383 
384  if ( widgetid.isNull() )
385  widgetid = defaultWidget;
386  return widgetid;
387 }
const char * className() const
Manages an editor widget Widget and wrapper share the same parent.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QgsMapLayer::LayerType type() const
Get the type of the layer.
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.
void setWidgetConfig(int attrIdx, const QgsEditorWidgetConfig &config)
Set the editor widget config for a field.
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:504
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:42
Container of fields for a vector layer.
Definition: qgsfield.h:193
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
void setWidgetType(int fieldIdx, const QString &widgetType)
Set the editor widget type for a field.
QString widgetType(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
QgsEditFormConfig * editFormConfig() const
Get the configuration of the form used to represent this vector layer.
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:108
bool isNull() const
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
void setLabelOnTop(int idx, bool onTop)
If this is set to true, the widget at the given index will receive its label on the previous line whi...
QDomElement toElement() const
virtual void clearWidget()
Clears the widget&#39;s current value and resets it back to the default state.
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.
virtual bool valid() const =0
Return true if the widget has been properly initialized.
bool inherits(const char *className) const
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.
QString name() const
Gets the name of the field.
Definition: qgsfield.cpp:84
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.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
QgsEditorWidgetConfig widgetConfig(int fieldIdx) const
Get the configuration for the editor widget used to represent the field at the given index...
int count() const
Return number of items.
Definition: qgsfield.cpp:365
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:44
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:385
QDomNode namedItem(const QString &name) const
bool isNull() const
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:505
const Key key(const T &value) const
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())
void setReadOnly(int idx, bool readOnly=true)
If set to false, the widget at the given index will be read-only.
static QgsMessageLog * instance()
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:388
iterator insert(const Key &key, const T &value)
QWidget * widget()
Access the widget managed by this wrapper.
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)
QObject * parent() const
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.
modularized edit widgets
static void initEditors(QgsMapCanvas *mapCanvas=nullptr, QgsMessageBar *messageBar=nullptr)
Registers all the default widgets.
QDomNode at(int index) const
const T value(const Key &key) const