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