1 /***************************************************************************
2 qgseditformconfig.cpp
3 ---------------------
4 begin : November 2015
5 copyright : (C) 2015 by 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
16 #include "qgseditformconfig_p.h"
17 #include "qgseditformconfig.h"
18 #include "qgsnetworkcontentfetcherregistry.h"
19 #include "qgspathresolver.h"
20 #include "qgsproject.h"
21 #include "qgsreadwritecontext.h"
22 #include "qgsrelationmanager.h"
23 #include "qgslogger.h"
24 #include "qgsxmlutils.h"
25 #include "qgsapplication.h"
26 #include "qgsmessagelog.h"
27 #include "qgsattributeeditorcontainer.h"
28 #include "qgsattributeeditorfield.h"
29 #include "qgsattributeeditorrelation.h"
30 #include <QUrl>
31
QgsEditFormConfig()32 QgsEditFormConfig::QgsEditFormConfig()
33 : d( new QgsEditFormConfigPrivate() )
34 {
35 }
36
setDataDefinedFieldProperties(const QString & fieldName,const QgsPropertyCollection & properties)37 void QgsEditFormConfig::setDataDefinedFieldProperties( const QString &fieldName, const QgsPropertyCollection &properties )
38 {
39 d.detach();
40 d->mDataDefinedFieldProperties[ fieldName ] = properties;
41 }
42
dataDefinedFieldProperties(const QString & fieldName) const43 QgsPropertyCollection QgsEditFormConfig::dataDefinedFieldProperties( const QString &fieldName ) const
44 {
45 return d->mDataDefinedFieldProperties.value( fieldName );
46 }
47
propertyDefinitions()48 const QgsPropertiesDefinition &QgsEditFormConfig::propertyDefinitions()
49 {
50 return QgsEditFormConfigPrivate::propertyDefinitions();
51 }
52
widgetConfig(const QString & widgetName) const53 QVariantMap QgsEditFormConfig::widgetConfig( const QString &widgetName ) const
54 {
55 const int fieldIndex = d->mFields.indexOf( widgetName );
56 if ( fieldIndex != -1 )
57 return d->mFields.at( fieldIndex ).editorWidgetSetup().config();
58 else
59 return d->mWidgetConfigs.value( widgetName );
60 }
61
setFields(const QgsFields & fields)62 void QgsEditFormConfig::setFields( const QgsFields &fields )
63 {
64 d.detach();
65 d->mFields = fields;
66
67 if ( !d->mConfiguredRootContainer )
68 {
69 d->mInvisibleRootContainer->clear();
70 for ( int i = 0; i < d->mFields.size(); ++i )
71 {
72 QgsAttributeEditorField *field = new QgsAttributeEditorField( d->mFields.at( i ).name(), i, d->mInvisibleRootContainer );
73 d->mInvisibleRootContainer->addChildElement( field );
74 }
75 }
76 }
77
onRelationsLoaded()78 void QgsEditFormConfig::onRelationsLoaded()
79 {
80 const QList<QgsAttributeEditorElement *> relations = d->mInvisibleRootContainer->findElements( QgsAttributeEditorElement::AeTypeRelation );
81
82 for ( QgsAttributeEditorElement *relElem : relations )
83 {
84 QgsAttributeEditorRelation *rel = dynamic_cast< QgsAttributeEditorRelation * >( relElem );
85 if ( !rel )
86 continue;
87
88 rel->init( QgsProject::instance()->relationManager() );
89 }
90 }
91
legacyUpdateRelationWidgetInTabs(QgsAttributeEditorContainer * container,const QString & widgetName,const QVariantMap & config)92 bool QgsEditFormConfig::legacyUpdateRelationWidgetInTabs( QgsAttributeEditorContainer *container, const QString &widgetName, const QVariantMap &config )
93 {
94 const QList<QgsAttributeEditorElement *> children = container->children();
95 for ( QgsAttributeEditorElement *child : children )
96 {
97 if ( child->type() == QgsAttributeEditorElement::AeTypeContainer )
98 {
99 QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( child );
100 if ( legacyUpdateRelationWidgetInTabs( container, widgetName, config ) )
101 {
102 //return when a relation has been set in a child or child child...
103 return true;
104 }
105 }
106 else if ( child->type() == QgsAttributeEditorElement::AeTypeRelation )
107 {
108 QgsAttributeEditorRelation *relation = dynamic_cast< QgsAttributeEditorRelation * >( child );
109 if ( relation )
110 {
111 if ( relation->relation().id() == widgetName )
112 {
113 if ( config.contains( QStringLiteral( "nm-rel" ) ) )
114 {
115 relation->setNmRelationId( config[QStringLiteral( "nm-rel" )] );
116 }
117 if ( config.contains( QStringLiteral( "force-suppress-popup" ) ) )
118 {
119 relation->setForceSuppressFormPopup( config[QStringLiteral( "force-suppress-popup" )].toBool() );
120 }
121 return true;
122 }
123 }
124 }
125 }
126 return false;
127 }
128
setWidgetConfig(const QString & widgetName,const QVariantMap & config)129 bool QgsEditFormConfig::setWidgetConfig( const QString &widgetName, const QVariantMap &config )
130 {
131 if ( d->mFields.indexOf( widgetName ) != -1 )
132 {
133 QgsDebugMsg( QStringLiteral( "Trying to set a widget config for a field on QgsEditFormConfig. Use layer->setEditorWidgetSetup() instead." ) );
134 return false;
135 }
136
137 //for legacy use it writes the relation editor configuration into the first instance of the widget
138 if ( config.contains( QStringLiteral( "force-suppress-popup" ) ) || config.contains( QStringLiteral( "nm-rel" ) ) )
139 {
140 QgsMessageLog::logMessage( QStringLiteral( "Deprecation Warning: Trying to set a relation config directly on the relation %1. Relation settings should be done for the specific widget instance instead. Use attributeEditorRelation->setNmRelationId() or attributeEditorRelation->setForceSuppressFormPopup() instead." ).arg( widgetName ) );
141 legacyUpdateRelationWidgetInTabs( d->mInvisibleRootContainer, widgetName, config );
142 }
143
144 d.detach();
145 d->mWidgetConfigs[widgetName] = config;
146 return true;
147 }
148
removeWidgetConfig(const QString & widgetName)149 bool QgsEditFormConfig::removeWidgetConfig( const QString &widgetName )
150 {
151 d.detach();
152 return d->mWidgetConfigs.remove( widgetName ) != 0;
153 }
154
QgsEditFormConfig(const QgsEditFormConfig & o)155 QgsEditFormConfig::QgsEditFormConfig( const QgsEditFormConfig &o ) //NOLINT
156 : d( o.d )
157 {
158 }
159
~QgsEditFormConfig()160 QgsEditFormConfig::~QgsEditFormConfig() //NOLINT
161 {}
162
operator =(const QgsEditFormConfig & o)163 QgsEditFormConfig &QgsEditFormConfig::operator=( const QgsEditFormConfig &o ) //NOLINT
164 {
165 d = o.d;
166 return *this;
167 }
168
operator ==(const QgsEditFormConfig & o)169 bool QgsEditFormConfig::operator==( const QgsEditFormConfig &o )
170 {
171 return d == o.d;
172 }
173
addTab(QgsAttributeEditorElement * data)174 void QgsEditFormConfig::addTab( QgsAttributeEditorElement *data )
175 {
176 d.detach();
177 d->mInvisibleRootContainer->addChildElement( data );
178 }
179
tabs() const180 QList<QgsAttributeEditorElement *> QgsEditFormConfig::tabs() const
181 {
182 return d->mInvisibleRootContainer->children();
183 }
184
clearTabs()185 void QgsEditFormConfig::clearTabs()
186 {
187 d.detach();
188 d->mInvisibleRootContainer->clear();
189 }
190
invisibleRootContainer()191 QgsAttributeEditorContainer *QgsEditFormConfig::invisibleRootContainer()
192 {
193 return d->mInvisibleRootContainer;
194 }
195
layout() const196 QgsEditFormConfig::EditorLayout QgsEditFormConfig::layout() const
197 {
198 return d->mEditorLayout;
199 }
200
setLayout(QgsEditFormConfig::EditorLayout editorLayout)201 void QgsEditFormConfig::setLayout( QgsEditFormConfig::EditorLayout editorLayout )
202 {
203 d.detach();
204 d->mEditorLayout = editorLayout;
205
206 if ( editorLayout == TabLayout )
207 d->mConfiguredRootContainer = true;
208 }
209
uiForm() const210 QString QgsEditFormConfig::uiForm() const
211 {
212 return d->mUiFormPath;
213 }
214
setUiForm(const QString & ui)215 void QgsEditFormConfig::setUiForm( const QString &ui )
216 {
217 if ( !ui.isEmpty() && !QUrl::fromUserInput( ui ).isLocalFile() )
218 {
219 // any existing download will not be restarted!
220 QgsApplication::instance()->networkContentFetcherRegistry()->fetch( ui, Qgis::ActionStart::Immediate );
221 }
222
223 if ( ui.isEmpty() )
224 {
225 setLayout( GeneratedLayout );
226 }
227 else
228 {
229 setLayout( UiFileLayout );
230 }
231 d->mUiFormPath = ui;
232 }
233
readOnly(int idx) const234 bool QgsEditFormConfig::readOnly( int idx ) const
235 {
236 if ( idx >= 0 && idx < d->mFields.count() )
237 {
238 if ( d->mFields.fieldOrigin( idx ) == QgsFields::OriginJoin
239 || d->mFields.fieldOrigin( idx ) == QgsFields::OriginExpression )
240 return true;
241 return !d->mFieldEditables.value( d->mFields.at( idx ).name(), true );
242 }
243 else
244 return false;
245 }
246
labelOnTop(int idx) const247 bool QgsEditFormConfig::labelOnTop( int idx ) const
248 {
249 if ( idx >= 0 && idx < d->mFields.count() )
250 return d->mLabelOnTop.value( d->mFields.at( idx ).name(), false );
251 else
252 return false;
253 }
254
setReadOnly(int idx,bool readOnly)255 void QgsEditFormConfig::setReadOnly( int idx, bool readOnly )
256 {
257 if ( idx >= 0 && idx < d->mFields.count() )
258 {
259 d.detach();
260 d->mFieldEditables[ d->mFields.at( idx ).name()] = !readOnly;
261 }
262 }
263
setLabelOnTop(int idx,bool onTop)264 void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
265 {
266 if ( idx >= 0 && idx < d->mFields.count() )
267 {
268 d.detach();
269 d->mLabelOnTop[ d->mFields.at( idx ).name()] = onTop;
270 }
271 }
272
reuseLastValue(int index) const273 bool QgsEditFormConfig::reuseLastValue( int index ) const
274 {
275 if ( index >= 0 && index < d->mFields.count() )
276 return d->mReuseLastValue.value( d->mFields.at( index ).name(), false );
277 else
278 return false;
279 }
280
setReuseLastValue(int index,bool reuse)281 void QgsEditFormConfig::setReuseLastValue( int index, bool reuse )
282 {
283 if ( index >= 0 && index < d->mFields.count() )
284 {
285 d.detach();
286 d->mReuseLastValue[ d->mFields.at( index ).name()] = reuse;
287 }
288 }
289
initFunction() const290 QString QgsEditFormConfig::initFunction() const
291 {
292 return d->mInitFunction;
293 }
294
setInitFunction(const QString & function)295 void QgsEditFormConfig::setInitFunction( const QString &function )
296 {
297 d.detach();
298 d->mInitFunction = function;
299 }
300
initCode() const301 QString QgsEditFormConfig::initCode() const
302 {
303 return d->mInitCode;
304 }
305
setInitCode(const QString & code)306 void QgsEditFormConfig::setInitCode( const QString &code )
307 {
308 d.detach();
309 d->mInitCode = code;
310 }
311
initFilePath() const312 QString QgsEditFormConfig::initFilePath() const
313 {
314 return d->mInitFilePath;
315 }
316
setInitFilePath(const QString & filePath)317 void QgsEditFormConfig::setInitFilePath( const QString &filePath )
318 {
319 d.detach();
320 d->mInitFilePath = filePath;
321
322 // if this is an URL, download file as there is a good chance it will be used later
323 if ( !filePath.isEmpty() && !QUrl::fromUserInput( filePath ).isLocalFile() )
324 {
325 // any existing download will not be restarted!
326 QgsApplication::instance()->networkContentFetcherRegistry()->fetch( filePath, Qgis::ActionStart::Immediate );
327 }
328 }
329
initCodeSource() const330 QgsEditFormConfig::PythonInitCodeSource QgsEditFormConfig::initCodeSource() const
331 {
332 return d->mInitCodeSource;
333 }
334
setInitCodeSource(const QgsEditFormConfig::PythonInitCodeSource initCodeSource)335 void QgsEditFormConfig::setInitCodeSource( const QgsEditFormConfig::PythonInitCodeSource initCodeSource )
336 {
337 d.detach();
338 d->mInitCodeSource = initCodeSource;
339 }
340
suppress() const341 QgsEditFormConfig::FeatureFormSuppress QgsEditFormConfig::suppress() const
342 {
343 return d->mSuppressForm;
344 }
345
setSuppress(QgsEditFormConfig::FeatureFormSuppress s)346 void QgsEditFormConfig::setSuppress( QgsEditFormConfig::FeatureFormSuppress s )
347 {
348 d.detach();
349 d->mSuppressForm = s;
350 }
351
readXml(const QDomNode & node,QgsReadWriteContext & context)352 void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &context )
353 {
354 const QgsReadWriteContextCategoryPopper p = context.enterCategory( QObject::tr( "Edit form config" ) );
355
356 d.detach();
357
358 const QDomNode editFormNode = node.namedItem( QStringLiteral( "editform" ) );
359 if ( !editFormNode.isNull() )
360 {
361 const QDomElement e = editFormNode.toElement();
362 const bool tolerantRemoteUrls = e.hasAttribute( QStringLiteral( "tolerant" ) );
363 if ( !e.text().isEmpty() )
364 {
365 const QString uiFormPath = context.pathResolver().readPath( e.text() );
366 // <= 3.2 had a bug where invalid ui paths would get written into projects on load
367 // to avoid restoring these invalid paths, we take a less-tolerant approach for older (untrustworthy) projects
368 // and only set ui forms paths IF they are local files OR start with "http(s)".
369 const bool localFile = QFileInfo::exists( uiFormPath );
370 if ( localFile || tolerantRemoteUrls || uiFormPath.startsWith( QLatin1String( "http" ) ) )
371 setUiForm( uiFormPath );
372 }
373 }
374
375 const QDomNode editFormInitNode = node.namedItem( QStringLiteral( "editforminit" ) );
376 if ( !editFormInitNode.isNull() )
377 {
378 d->mInitFunction = editFormInitNode.toElement().text();
379 }
380
381 const QDomNode editFormInitCodeSourceNode = node.namedItem( QStringLiteral( "editforminitcodesource" ) );
382 if ( !editFormInitCodeSourceNode.isNull() && !editFormInitCodeSourceNode.toElement().text().isEmpty() )
383 {
384 setInitCodeSource( static_cast< QgsEditFormConfig::PythonInitCodeSource >( editFormInitCodeSourceNode.toElement().text().toInt() ) );
385 }
386
387 const QDomNode editFormInitCodeNode = node.namedItem( QStringLiteral( "editforminitcode" ) );
388 if ( !editFormInitCodeNode.isNull() )
389 {
390 setInitCode( editFormInitCodeNode.toElement().text() );
391 }
392
393 // Temporary < 2.12 b/w compatibility "dot" support patch
394 // \see: https://github.com/qgis/QGIS/pull/2498
395 // For b/w compatibility, check if there's a dot in the function name
396 // and if yes, transform it in an import statement for the module
397 // and set the PythonInitCodeSource to CodeSourceDialog
398 const int dotPos = d->mInitFunction.lastIndexOf( '.' );
399 if ( dotPos >= 0 ) // It's a module
400 {
401 setInitCodeSource( QgsEditFormConfig::CodeSourceDialog );
402 setInitCode( QStringLiteral( "from %1 import %2\n" ).arg( d->mInitFunction.left( dotPos ), d->mInitFunction.mid( dotPos + 1 ) ) );
403 setInitFunction( d->mInitFunction.mid( dotPos + 1 ) );
404 }
405
406 const QDomNode editFormInitFilePathNode = node.namedItem( QStringLiteral( "editforminitfilepath" ) );
407 if ( !editFormInitFilePathNode.isNull() && !editFormInitFilePathNode.toElement().text().isEmpty() )
408 {
409 setInitFilePath( context.pathResolver().readPath( editFormInitFilePathNode.toElement().text() ) );
410 }
411
412 const QDomNode fFSuppNode = node.namedItem( QStringLiteral( "featformsuppress" ) );
413 if ( fFSuppNode.isNull() )
414 {
415 d->mSuppressForm = QgsEditFormConfig::SuppressDefault;
416 }
417 else
418 {
419 const QDomElement e = fFSuppNode.toElement();
420 d->mSuppressForm = static_cast< QgsEditFormConfig::FeatureFormSuppress >( e.text().toInt() );
421 }
422
423 // tab display
424 const QDomNode editorLayoutNode = node.namedItem( QStringLiteral( "editorlayout" ) );
425 if ( editorLayoutNode.isNull() )
426 {
427 d->mEditorLayout = QgsEditFormConfig::GeneratedLayout;
428 }
429 else
430 {
431 if ( editorLayoutNode.toElement().text() == QLatin1String( "uifilelayout" ) )
432 {
433 d->mEditorLayout = QgsEditFormConfig::UiFileLayout;
434 }
435 else if ( editorLayoutNode.toElement().text() == QLatin1String( "tablayout" ) )
436 {
437 d->mEditorLayout = QgsEditFormConfig::TabLayout;
438 }
439 else
440 {
441 d->mEditorLayout = QgsEditFormConfig::GeneratedLayout;
442 }
443 }
444
445 d->mFieldEditables.clear();
446 const QDomNodeList editableNodeList = node.namedItem( QStringLiteral( "editable" ) ).toElement().childNodes();
447 for ( int i = 0; i < editableNodeList.size(); ++i )
448 {
449 const QDomElement editableElement = editableNodeList.at( i ).toElement();
450 d->mFieldEditables.insert( editableElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( editableElement.attribute( QStringLiteral( "editable" ) ).toInt() ) );
451 }
452
453 d->mLabelOnTop.clear();
454 const QDomNodeList labelOnTopNodeList = node.namedItem( QStringLiteral( "labelOnTop" ) ).toElement().childNodes();
455 for ( int i = 0; i < labelOnTopNodeList.size(); ++i )
456 {
457 const QDomElement labelOnTopElement = labelOnTopNodeList.at( i ).toElement();
458 d->mLabelOnTop.insert( labelOnTopElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( labelOnTopElement.attribute( QStringLiteral( "labelOnTop" ) ).toInt() ) );
459 }
460
461 d->mReuseLastValue.clear();
462 const QDomNodeList reuseLastValueNodeList = node.namedItem( QStringLiteral( "reuseLastValue" ) ).toElement().childNodes();
463 for ( int i = 0; i < reuseLastValueNodeList.size(); ++i )
464 {
465 const QDomElement reuseLastValueElement = reuseLastValueNodeList.at( i ).toElement();
466 d->mReuseLastValue.insert( reuseLastValueElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( reuseLastValueElement.attribute( QStringLiteral( "reuseLastValue" ) ).toInt() ) );
467 }
468
469 // Read data defined field properties
470 const QDomNodeList fieldDDPropertiesNodeList = node.namedItem( QStringLiteral( "dataDefinedFieldProperties" ) ).toElement().childNodes();
471 for ( int i = 0; i < fieldDDPropertiesNodeList.size(); ++i )
472 {
473 const QDomElement DDElement = fieldDDPropertiesNodeList.at( i ).toElement();
474 QgsPropertyCollection collection;
475 collection.readXml( DDElement, propertyDefinitions() );
476 d->mDataDefinedFieldProperties.insert( DDElement.attribute( QStringLiteral( "name" ) ), collection );
477 }
478
479 const QDomNodeList widgetsNodeList = node.namedItem( QStringLiteral( "widgets" ) ).toElement().childNodes();
480
481 for ( int i = 0; i < widgetsNodeList.size(); ++i )
482 {
483 const QDomElement widgetElement = widgetsNodeList.at( i ).toElement();
484 const QVariant config = QgsXmlUtils::readVariant( widgetElement.firstChildElement( QStringLiteral( "config" ) ) );
485
486 d->mWidgetConfigs[widgetElement.attribute( QStringLiteral( "name" ) )] = config.toMap();
487 }
488
489 // tabs and groups display info
490 const QDomNode attributeEditorFormNode = node.namedItem( QStringLiteral( "attributeEditorForm" ) );
491 if ( !attributeEditorFormNode.isNull() )
492 {
493 const QDomNodeList attributeEditorFormNodeList = attributeEditorFormNode.toElement().childNodes();
494
495 if ( attributeEditorFormNodeList.size() )
496 {
497 d->mConfiguredRootContainer = true;
498 clearTabs();
499
500 for ( int i = 0; i < attributeEditorFormNodeList.size(); i++ )
501 {
502 QDomElement elem = attributeEditorFormNodeList.at( i ).toElement();
503
504 fixLegacyConfig( elem );
505
506 const QString layerId = node.namedItem( QStringLiteral( "id" ) ).toElement().text();
507 QgsAttributeEditorElement *attributeEditorWidget = QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, nullptr );
508 if ( attributeEditorWidget )
509 addTab( attributeEditorWidget );
510 }
511
512 onRelationsLoaded();
513 }
514 }
515 }
516
fixLegacyConfig(QDomElement & el)517 void QgsEditFormConfig::fixLegacyConfig( QDomElement &el )
518 {
519 // recursive method to move widget config into attribute element config
520
521 if ( el.tagName() == QLatin1String( "attributeEditorRelation" ) )
522 {
523 if ( !el.hasAttribute( QStringLiteral( "forceSuppressFormPopup" ) ) )
524 {
525 // pre QGIS 3.16 compatibility - the widgets section is read before
526 const bool forceSuppress = widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "force-suppress-popup" ), false ).toBool();
527 el.setAttribute( QStringLiteral( "forceSuppressFormPopup" ), forceSuppress ? 1 : 0 );
528 }
529 if ( !el.hasAttribute( QStringLiteral( "nmRelationId" ) ) )
530 {
531 // pre QGIS 3.16 compatibility - the widgets section is read before
532 el.setAttribute( QStringLiteral( "nmRelationId" ), widgetConfig( el.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "nm-rel" ) ).toString() );
533 }
534 }
535
536 const QDomNodeList children = el.childNodes();
537 for ( int i = 0; i < children.size(); i++ )
538 {
539 QDomElement child = children.at( i ).toElement();
540 fixLegacyConfig( child );
541 el.replaceChild( child, children.at( i ) );
542 }
543 }
544
writeXml(QDomNode & node,const QgsReadWriteContext & context) const545 void QgsEditFormConfig::writeXml( QDomNode &node, const QgsReadWriteContext &context ) const
546 {
547 QDomDocument doc( node.ownerDocument() );
548
549 QDomElement efField = doc.createElement( QStringLiteral( "editform" ) );
550 efField.setAttribute( QStringLiteral( "tolerant" ), QStringLiteral( "1" ) );
551 const QDomText efText = doc.createTextNode( context.pathResolver().writePath( uiForm() ) );
552 efField.appendChild( efText );
553 node.appendChild( efField );
554
555 QDomElement efiField = doc.createElement( QStringLiteral( "editforminit" ) );
556 if ( !initFunction().isEmpty() )
557 efiField.appendChild( doc.createTextNode( initFunction() ) );
558 node.appendChild( efiField );
559
560 QDomElement eficsField = doc.createElement( QStringLiteral( "editforminitcodesource" ) );
561 eficsField.appendChild( doc.createTextNode( QString::number( initCodeSource() ) ) );
562 node.appendChild( eficsField );
563
564 QDomElement efifpField = doc.createElement( QStringLiteral( "editforminitfilepath" ) );
565 efifpField.appendChild( doc.createTextNode( context.pathResolver().writePath( initFilePath() ) ) );
566 node.appendChild( efifpField );
567
568 QDomElement eficField = doc.createElement( QStringLiteral( "editforminitcode" ) );
569 eficField.appendChild( doc.createCDATASection( initCode() ) );
570 node.appendChild( eficField );
571
572 QDomElement fFSuppElem = doc.createElement( QStringLiteral( "featformsuppress" ) );
573 const QDomText fFSuppText = doc.createTextNode( QString::number( suppress() ) );
574 fFSuppElem.appendChild( fFSuppText );
575 node.appendChild( fFSuppElem );
576
577 // tab display
578 QDomElement editorLayoutElem = doc.createElement( QStringLiteral( "editorlayout" ) );
579 switch ( layout() )
580 {
581 case QgsEditFormConfig::UiFileLayout:
582 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "uifilelayout" ) ) );
583 break;
584
585 case QgsEditFormConfig::TabLayout:
586 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "tablayout" ) ) );
587 break;
588
589 case QgsEditFormConfig::GeneratedLayout:
590 default:
591 editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "generatedlayout" ) ) );
592 break;
593 }
594
595 node.appendChild( editorLayoutElem );
596
597 // tabs and groups of edit form
598 if ( !tabs().empty() && d->mConfiguredRootContainer )
599 {
600 QDomElement tabsElem = doc.createElement( QStringLiteral( "attributeEditorForm" ) );
601 const QDomElement rootElem = d->mInvisibleRootContainer->toDomElement( doc );
602 const QDomNodeList elemList = rootElem.childNodes();
603 while ( !elemList.isEmpty() )
604 {
605 tabsElem.appendChild( elemList.at( 0 ) );
606 }
607 node.appendChild( tabsElem );
608 }
609
610 QDomElement editableElem = doc.createElement( QStringLiteral( "editable" ) );
611 for ( auto editIt = d->mFieldEditables.constBegin(); editIt != d->mFieldEditables.constEnd(); ++editIt )
612 {
613 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
614 fieldElem.setAttribute( QStringLiteral( "name" ), editIt.key() );
615 fieldElem.setAttribute( QStringLiteral( "editable" ), editIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
616 editableElem.appendChild( fieldElem );
617 }
618 node.appendChild( editableElem );
619
620 QDomElement labelOnTopElem = doc.createElement( QStringLiteral( "labelOnTop" ) );
621 for ( auto labelOnTopIt = d->mLabelOnTop.constBegin(); labelOnTopIt != d->mLabelOnTop.constEnd(); ++labelOnTopIt )
622 {
623 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
624 fieldElem.setAttribute( QStringLiteral( "name" ), labelOnTopIt.key() );
625 fieldElem.setAttribute( QStringLiteral( "labelOnTop" ), labelOnTopIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
626 labelOnTopElem.appendChild( fieldElem );
627 }
628 node.appendChild( labelOnTopElem );
629
630 QDomElement reuseLastValueElem = doc.createElement( QStringLiteral( "reuseLastValue" ) );
631 for ( auto reuseLastValueIt = d->mReuseLastValue.constBegin(); reuseLastValueIt != d->mReuseLastValue.constEnd(); ++reuseLastValueIt )
632 {
633 QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
634 fieldElem.setAttribute( QStringLiteral( "name" ), reuseLastValueIt.key() );
635 fieldElem.setAttribute( QStringLiteral( "reuseLastValue" ), reuseLastValueIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
636 reuseLastValueElem.appendChild( fieldElem );
637 }
638 node.appendChild( reuseLastValueElem );
639
640 // Store data defined field properties
641 QDomElement ddFieldPropsElement = doc.createElement( QStringLiteral( "dataDefinedFieldProperties" ) );
642 for ( auto it = d->mDataDefinedFieldProperties.constBegin(); it != d->mDataDefinedFieldProperties.constEnd(); ++it )
643 {
644 QDomElement ddPropsElement = doc.createElement( QStringLiteral( "field" ) );
645 ddPropsElement.setAttribute( QStringLiteral( "name" ), it.key() );
646 it.value().writeXml( ddPropsElement, propertyDefinitions() );
647 ddFieldPropsElement.appendChild( ddPropsElement );
648 }
649 node.appendChild( ddFieldPropsElement );
650
651 QDomElement widgetsElem = doc.createElement( QStringLiteral( "widgets" ) );
652
653 QMap<QString, QVariantMap >::ConstIterator configIt( d->mWidgetConfigs.constBegin() );
654
655 while ( configIt != d->mWidgetConfigs.constEnd() )
656 {
657 QDomElement widgetElem = doc.createElement( QStringLiteral( "widget" ) );
658 widgetElem.setAttribute( QStringLiteral( "name" ), configIt.key() );
659 // widgetElem.setAttribute( "notNull", );
660
661 QDomElement configElem = QgsXmlUtils::writeVariant( configIt.value(), doc );
662 configElem.setTagName( QStringLiteral( "config" ) );
663 widgetElem.appendChild( configElem );
664 widgetsElem.appendChild( widgetElem );
665 ++configIt;
666 }
667
668 node.appendChild( widgetsElem );
669 }
670
attributeEditorElementFromDomElement(QDomElement & elem,QgsAttributeEditorElement * parent,const QString & layerId,const QgsReadWriteContext & context)671 QgsAttributeEditorElement *QgsEditFormConfig::attributeEditorElementFromDomElement( QDomElement &elem, QgsAttributeEditorElement *parent, const QString &layerId, const QgsReadWriteContext &context )
672 {
673 return QgsAttributeEditorElement::create( elem, layerId, d->mFields, context, parent );
674 }
675