QGIS API Documentation  2.14.0-Essen
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  return ww;
135  }
136  }
137  return nullptr;
138 }
139 
141 {
142  if ( mWidgetFactories.contains( widgetId ) )
143  {
144  return mWidgetFactories[widgetId]->configWidget( vl, fieldIdx, parent );
145  }
146  return nullptr;
147 }
148 
150 {
151  if ( mWidgetFactories.contains( widgetId ) )
152  {
153  return mWidgetFactories[widgetId]->name();
154  }
155 
156  return QString();
157 }
158 
160 {
161  return mWidgetFactories;
162 }
163 
165 {
166  return mWidgetFactories.value( widgetId );
167 }
168 
170 {
171  if ( !widgetFactory )
172  {
173  QgsMessageLog::instance()->logMessage( "QgsEditorWidgetRegistry: Factory not valid." );
174  return false;
175  }
176  else if ( mWidgetFactories.contains( widgetId ) )
177  {
178  QgsMessageLog::instance()->logMessage( QString( "QgsEditorWidgetRegistry: Factory with id %1 already registered." ).arg( widgetId ) );
179  return false;
180  }
181  else
182  {
183  mWidgetFactories.insert( widgetId, widgetFactory );
184 
185  // Use this factory as default where it provides the heighest priority
186  QMap<const char*, int> types = widgetFactory->supportedWidgetTypes();
188  it = types.constBegin();
189 
190  for ( ; it != types.constEnd(); ++it )
191  {
192  if ( it.value() > mFactoriesByType[it.key()].first )
193  {
194  mFactoriesByType[it.key()] = qMakePair( it.value(), widgetId );
195  }
196  }
197 
198  return true;
199  }
200 }
201 
202 void QgsEditorWidgetRegistry::readMapLayer( QgsMapLayer* mapLayer, const QDomElement& layerElem )
203 {
204  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
205  {
206  return;
207  }
208 
209  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
210  Q_ASSERT( vectorLayer );
211 
212  QDomNodeList editTypeNodes = layerElem.namedItem( "edittypes" ).childNodes();
213 
214  for ( int i = 0; i < editTypeNodes.size(); i++ )
215  {
216  QDomNode editTypeNode = editTypeNodes.at( i );
217  QDomElement editTypeElement = editTypeNode.toElement();
218 
219  QString name = editTypeElement.attribute( "name" );
220 
221  int idx = vectorLayer->fieldNameIndex( name );
222  if ( idx == -1 )
223  continue;
224 
225  bool hasLegacyType;
226  QgsVectorLayer::EditType editType =
227  ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt( &hasLegacyType );
228 
229  QString ewv2Type;
231 
232  if ( hasLegacyType && editType != QgsVectorLayer::EditorWidgetV2 )
233  {
235  ewv2Type = readLegacyConfig( vectorLayer, editTypeElement, cfg );
237  }
238  else
239  ewv2Type = editTypeElement.attribute( "widgetv2type" );
240 
241  if ( mWidgetFactories.contains( ewv2Type ) )
242  {
243  vectorLayer->editFormConfig()->setWidgetType( idx, ewv2Type );
244  QDomElement ewv2CfgElem = editTypeElement.namedItem( "widgetv2config" ).toElement();
245 
246  if ( !ewv2CfgElem.isNull() )
247  {
248  cfg = mWidgetFactories[ewv2Type]->readEditorConfig( ewv2CfgElem, vectorLayer, idx );
249  }
250 
251  vectorLayer->editFormConfig()->setReadOnly( idx, ewv2CfgElem.attribute( "fieldEditable", "1" ) != "1" );
252  vectorLayer->editFormConfig()->setLabelOnTop( idx, ewv2CfgElem.attribute( "labelOnTop", "0" ) == "1" );
253  vectorLayer->editFormConfig()->setWidgetConfig( idx, cfg );
254  }
255  else
256  {
257  QgsMessageLog::logMessage( tr( "Unknown attribute editor widget '%1'" ).arg( ewv2Type ) );
258  }
259  }
260 }
261 
262 const QString QgsEditorWidgetRegistry::readLegacyConfig( QgsVectorLayer* vl, const QDomElement& editTypeElement, QgsEditorWidgetConfig& cfg )
263 {
264  QString name = editTypeElement.attribute( "name" );
265 
266  QgsVectorLayer::EditType editType = ( QgsVectorLayer::EditType ) editTypeElement.attribute( "type" ).toInt();
267 
269  return QgsLegacyHelpers::convertEditType( editType, cfg, vl, name, editTypeElement );
271 }
272 
273 void QgsEditorWidgetRegistry::writeMapLayer( QgsMapLayer* mapLayer, QDomElement& layerElem, QDomDocument& doc ) const
274 {
275  if ( mapLayer->type() != QgsMapLayer::VectorLayer )
276  {
277  return;
278  }
279 
280  QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
281  if ( !vectorLayer )
282  {
283  return;
284  }
285 
286  QDomNode editTypesNode = doc.createElement( "edittypes" );
287 
288  QgsFields fields = vectorLayer->fields();
289  for ( int idx = 0; idx < fields.count(); ++idx )
290  {
291  const QgsField &field = fields.at( idx );
292  const QString& widgetType = vectorLayer->editFormConfig()->widgetType( idx );
293  if ( !mWidgetFactories.contains( widgetType ) )
294  {
295  QgsMessageLog::logMessage( tr( "Could not save unknown editor widget type '%1'." ).arg( widgetType ) );
296  continue;
297  }
298 
299 
300  QDomElement editTypeElement = doc.createElement( "edittype" );
301  editTypeElement.setAttribute( "name", field.name() );
302  editTypeElement.setAttribute( "widgetv2type", widgetType );
303 
304  if ( mWidgetFactories.contains( widgetType ) )
305  {
306  QDomElement ewv2CfgElem = doc.createElement( "widgetv2config" );
307  ewv2CfgElem.setAttribute( "fieldEditable", !vectorLayer->editFormConfig()->readOnly( idx ) );
308  ewv2CfgElem.setAttribute( "labelOnTop", vectorLayer->editFormConfig()->labelOnTop( idx ) );
309 
310  mWidgetFactories[widgetType]->writeConfig( vectorLayer->editFormConfig()->widgetConfig( idx ), ewv2CfgElem, doc, vectorLayer, idx );
311 
312  editTypeElement.appendChild( ewv2CfgElem );
313  }
314 
315  editTypesNode.appendChild( editTypeElement );
316  }
317 
318  layerElem.appendChild( editTypesNode );
319 }
320 
321 void QgsEditorWidgetRegistry::mapLayerAdded( QgsMapLayer* mapLayer )
322 {
323  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mapLayer );
324 
325  if ( vl )
326  {
327  connect( vl, SIGNAL( readCustomSymbology( const QDomElement&, QString& ) ), this, SLOT( readSymbology( const QDomElement&, QString& ) ) );
328  connect( vl, SIGNAL( writeCustomSymbology( QDomElement&, QDomDocument&, QString& ) ), this, SLOT( writeSymbology( QDomElement&, QDomDocument&, QString& ) ) );
329  }
330 }
331 
332 void QgsEditorWidgetRegistry::readSymbology( const QDomElement& element, QString& errorMessage )
333 {
334  Q_UNUSED( errorMessage )
335  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
336 
337  Q_ASSERT( vl );
338 
339  readMapLayer( vl, element );
340 }
341 
342 void QgsEditorWidgetRegistry::writeSymbology( QDomElement& element, QDomDocument& doc, QString& errorMessage )
343 {
344  Q_UNUSED( errorMessage )
345  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( sender() );
346 
347  Q_ASSERT( vl );
348 
349  writeMapLayer( vl, element, doc );
350 }
351 
352 QString QgsEditorWidgetRegistry::findSuitableWrapper( QWidget* editor, const QString& defaultWidget )
353 {
354  QMap<const char*, QPair<int, QString> >::ConstIterator it;
355 
356  QString widgetid;
357 
358  // Editor can be null
359  if ( editor )
360  {
361  int weight = 0;
362 
363  it = mFactoriesByType.constBegin();
364  for ( ; it != mFactoriesByType.constEnd(); ++it )
365  {
366  if ( editor->staticMetaObject.className() == it.key() )
367  {
368  // if it's a perfect match: return it directly
369  return it.value().second;
370  }
371  else if ( editor->inherits( it.key() ) )
372  {
373  // if it's a subclass, continue evaluating, maybe we find a more-specific or one with more weight
374  if ( it.value().first > weight )
375  {
376  weight = it.value().first;
377  widgetid = it.value().second;
378  }
379  }
380  }
381  }
382 
383  if ( widgetid.isNull() )
384  widgetid = defaultWidget;
385  return widgetid;
386 }
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.
Definition: qgsmaplayer.cpp:99
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:407
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:42
Container of fields for a vector layer.
Definition: qgsfield.h:187
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:105
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
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:408
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:381
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