QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgseditformconfig.cpp
Go to the documentation of this file.
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 #include "qgseditformconfig_p.h"
16 #include "qgseditformconfig.h"
18 #include "qgspathresolver.h"
19 #include "qgsproject.h"
20 #include "qgsreadwritecontext.h"
21 #include "qgsrelationmanager.h"
22 #include "qgslogger.h"
23 #include "qgsxmlutils.h"
24 #include "qgsapplication.h"
25 #include "qgsmessagelog.h"
26 
28 {
29  qDeleteAll( mChildren );
30 }
31 
33  : d( new QgsEditFormConfigPrivate() )
34 {
35 }
36 
37 void QgsEditFormConfig::setDataDefinedFieldProperties( const QString &fieldName, const QgsPropertyCollection &properties )
38 {
39  d.detach();
40  d->mDataDefinedFieldProperties[ fieldName ] = properties;
41 }
42 
44 {
45  return d->mDataDefinedFieldProperties.value( fieldName );
46 }
47 
49 {
50  return QgsEditFormConfigPrivate::propertyDefinitions();
51 }
52 
53 QVariantMap QgsEditFormConfig::widgetConfig( const QString &widgetName ) const
54 {
55  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 
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 
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 
89  }
90 }
91 
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 
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 
149 bool QgsEditFormConfig::removeWidgetConfig( const QString &widgetName )
150 {
151  d.detach();
152  return d->mWidgetConfigs.remove( widgetName ) != 0;
153 }
154 
156  : d( o.d )
157 {
158 }
159 
161 {}
162 
164 {
165  d = o.d;
166  return *this;
167 }
168 
170 {
171  return d == o.d;
172 }
173 
175 {
176  d.detach();
177  d->mInvisibleRootContainer->addChildElement( data );
178 }
179 
180 QList<QgsAttributeEditorElement *> QgsEditFormConfig::tabs() const
181 {
182  return d->mInvisibleRootContainer->children();
183 }
184 
186 {
187  d.detach();
188  d->mInvisibleRootContainer->clear();
189 }
190 
192 {
193  return d->mInvisibleRootContainer;
194 }
195 
197 {
198  return d->mEditorLayout;
199 }
200 
202 {
203  d.detach();
204  d->mEditorLayout = editorLayout;
205 
206  if ( editorLayout == TabLayout )
207  d->mConfiguredRootContainer = true;
208 }
209 
211 {
212  return d->mUiFormPath;
213 }
214 
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!
221  }
222 
223  if ( ui.isEmpty() )
224  {
226  }
227  else
228  {
230  }
231  d->mUiFormPath = ui;
232 }
233 
234 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 
247 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 
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 
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 
274 {
275  return d->mInitFunction;
276 }
277 
278 void QgsEditFormConfig::setInitFunction( const QString &function )
279 {
280  d.detach();
281  d->mInitFunction = function;
282 }
283 
285 {
286  return d->mInitCode;
287 }
288 
289 void QgsEditFormConfig::setInitCode( const QString &code )
290 {
291  d.detach();
292  d->mInitCode = code;
293 }
294 
296 {
297  return d->mInitFilePath;
298 }
299 
300 void QgsEditFormConfig::setInitFilePath( const QString &filePath )
301 {
302  d.detach();
303  d->mInitFilePath = filePath;
304 
305  // if this is an URL, download file as there is a good chance it will be used later
306  if ( !filePath.isEmpty() && !QUrl::fromUserInput( filePath ).isLocalFile() )
307  {
308  // any existing download will not be restarted!
310  }
311 }
312 
314 {
315  return d->mInitCodeSource;
316 }
317 
319 {
320  d.detach();
321  d->mInitCodeSource = initCodeSource;
322 }
323 
325 {
326  return d->mSuppressForm;
327 }
328 
330 {
331  d.detach();
332  d->mSuppressForm = s;
333 }
334 
335 void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &context )
336 {
337  QgsReadWriteContextCategoryPopper p = context.enterCategory( QObject::tr( "Edit form config" ) );
338 
339  d.detach();
340 
341  QDomNode editFormNode = node.namedItem( QStringLiteral( "editform" ) );
342  if ( !editFormNode.isNull() )
343  {
344  QDomElement e = editFormNode.toElement();
345  const bool tolerantRemoteUrls = e.hasAttribute( QStringLiteral( "tolerant" ) );
346  if ( !e.text().isEmpty() )
347  {
348  const QString uiFormPath = context.pathResolver().readPath( e.text() );
349  // <= 3.2 had a bug where invalid ui paths would get written into projects on load
350  // to avoid restoring these invalid paths, we take a less-tolerant approach for older (untrustworthy) projects
351  // and only set ui forms paths IF they are local files OR start with "http(s)".
352  const bool localFile = QFileInfo::exists( uiFormPath );
353  if ( localFile || tolerantRemoteUrls || uiFormPath.startsWith( QLatin1String( "http" ) ) )
354  setUiForm( uiFormPath );
355  }
356  }
357 
358  QDomNode editFormInitNode = node.namedItem( QStringLiteral( "editforminit" ) );
359  if ( !editFormInitNode.isNull() )
360  {
361  d->mInitFunction = editFormInitNode.toElement().text();
362  }
363 
364  QDomNode editFormInitCodeSourceNode = node.namedItem( QStringLiteral( "editforminitcodesource" ) );
365  if ( !editFormInitCodeSourceNode.isNull() && !editFormInitCodeSourceNode.toElement().text().isEmpty() )
366  {
367  setInitCodeSource( static_cast< QgsEditFormConfig::PythonInitCodeSource >( editFormInitCodeSourceNode.toElement().text().toInt() ) );
368  }
369 
370  QDomNode editFormInitCodeNode = node.namedItem( QStringLiteral( "editforminitcode" ) );
371  if ( !editFormInitCodeNode.isNull() )
372  {
373  setInitCode( editFormInitCodeNode.toElement().text() );
374  }
375 
376  // Temporary < 2.12 b/w compatibility "dot" support patch
377  // \see: https://github.com/qgis/QGIS/pull/2498
378  // For b/w compatibility, check if there's a dot in the function name
379  // and if yes, transform it in an import statement for the module
380  // and set the PythonInitCodeSource to CodeSourceDialog
381  int dotPos = d->mInitFunction.lastIndexOf( '.' );
382  if ( dotPos >= 0 ) // It's a module
383  {
385  setInitCode( QStringLiteral( "from %1 import %2\n" ).arg( d->mInitFunction.left( dotPos ), d->mInitFunction.mid( dotPos + 1 ) ) );
386  setInitFunction( d->mInitFunction.mid( dotPos + 1 ) );
387  }
388 
389  QDomNode editFormInitFilePathNode = node.namedItem( QStringLiteral( "editforminitfilepath" ) );
390  if ( !editFormInitFilePathNode.isNull() && !editFormInitFilePathNode.toElement().text().isEmpty() )
391  {
392  setInitFilePath( context.pathResolver().readPath( editFormInitFilePathNode.toElement().text() ) );
393  }
394 
395  QDomNode fFSuppNode = node.namedItem( QStringLiteral( "featformsuppress" ) );
396  if ( fFSuppNode.isNull() )
397  {
398  d->mSuppressForm = QgsEditFormConfig::SuppressDefault;
399  }
400  else
401  {
402  QDomElement e = fFSuppNode.toElement();
403  d->mSuppressForm = static_cast< QgsEditFormConfig::FeatureFormSuppress >( e.text().toInt() );
404  }
405 
406  // tab display
407  QDomNode editorLayoutNode = node.namedItem( QStringLiteral( "editorlayout" ) );
408  if ( editorLayoutNode.isNull() )
409  {
410  d->mEditorLayout = QgsEditFormConfig::GeneratedLayout;
411  }
412  else
413  {
414  if ( editorLayoutNode.toElement().text() == QLatin1String( "uifilelayout" ) )
415  {
416  d->mEditorLayout = QgsEditFormConfig::UiFileLayout;
417  }
418  else if ( editorLayoutNode.toElement().text() == QLatin1String( "tablayout" ) )
419  {
420  d->mEditorLayout = QgsEditFormConfig::TabLayout;
421  }
422  else
423  {
424  d->mEditorLayout = QgsEditFormConfig::GeneratedLayout;
425  }
426  }
427 
428  d->mFieldEditables.clear();
429  QDomNodeList editableNodeList = node.namedItem( QStringLiteral( "editable" ) ).toElement().childNodes();
430  for ( int i = 0; i < editableNodeList.size(); ++i )
431  {
432  QDomElement editableElement = editableNodeList.at( i ).toElement();
433  d->mFieldEditables.insert( editableElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( editableElement.attribute( QStringLiteral( "editable" ) ).toInt() ) );
434  }
435 
436  d->mLabelOnTop.clear();
437  QDomNodeList labelOnTopNodeList = node.namedItem( QStringLiteral( "labelOnTop" ) ).toElement().childNodes();
438  for ( int i = 0; i < labelOnTopNodeList.size(); ++i )
439  {
440  QDomElement labelOnTopElement = labelOnTopNodeList.at( i ).toElement();
441  d->mLabelOnTop.insert( labelOnTopElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( labelOnTopElement.attribute( QStringLiteral( "labelOnTop" ) ).toInt() ) );
442  }
443 
444  // Read data defined field properties
445  QDomNodeList fieldDDPropertiesNodeList = node.namedItem( QStringLiteral( "dataDefinedFieldProperties" ) ).toElement().childNodes();
446  for ( int i = 0; i < fieldDDPropertiesNodeList.size(); ++i )
447  {
448  QDomElement DDElement = fieldDDPropertiesNodeList.at( i ).toElement();
449  QgsPropertyCollection collection;
450  collection.readXml( DDElement, propertyDefinitions() );
451  d->mDataDefinedFieldProperties.insert( DDElement.attribute( QStringLiteral( "name" ) ), collection );
452  }
453 
454  QDomNodeList widgetsNodeList = node.namedItem( QStringLiteral( "widgets" ) ).toElement().childNodes();
455 
456  for ( int i = 0; i < widgetsNodeList.size(); ++i )
457  {
458  QDomElement widgetElement = widgetsNodeList.at( i ).toElement();
459  QVariant config = QgsXmlUtils::readVariant( widgetElement.firstChildElement( QStringLiteral( "config" ) ) );
460 
461  d->mWidgetConfigs[widgetElement.attribute( QStringLiteral( "name" ) )] = config.toMap();
462  }
463 
464  // tabs and groups display info
465  QDomNode attributeEditorFormNode = node.namedItem( QStringLiteral( "attributeEditorForm" ) );
466  if ( !attributeEditorFormNode.isNull() )
467  {
468  QDomNodeList attributeEditorFormNodeList = attributeEditorFormNode.toElement().childNodes();
469 
470  if ( attributeEditorFormNodeList.size() )
471  {
472  d->mConfiguredRootContainer = true;
473  clearTabs();
474 
475  for ( int i = 0; i < attributeEditorFormNodeList.size(); i++ )
476  {
477  QDomElement elem = attributeEditorFormNodeList.at( i ).toElement();
478 
479  QgsAttributeEditorElement *attributeEditorWidget = attributeEditorElementFromDomElement( elem, nullptr, node.namedItem( QStringLiteral( "id" ) ).toElement().text(), context );
480  if ( attributeEditorWidget )
481  addTab( attributeEditorWidget );
482  }
483 
484  onRelationsLoaded();
485  }
486  }
487 }
488 
489 void QgsEditFormConfig::writeXml( QDomNode &node, const QgsReadWriteContext &context ) const
490 {
491  QDomDocument doc( node.ownerDocument() );
492 
493  QDomElement efField = doc.createElement( QStringLiteral( "editform" ) );
494  efField.setAttribute( QStringLiteral( "tolerant" ), QStringLiteral( "1" ) );
495  QDomText efText = doc.createTextNode( context.pathResolver().writePath( uiForm() ) );
496  efField.appendChild( efText );
497  node.appendChild( efField );
498 
499  QDomElement efiField = doc.createElement( QStringLiteral( "editforminit" ) );
500  if ( !initFunction().isEmpty() )
501  efiField.appendChild( doc.createTextNode( initFunction() ) );
502  node.appendChild( efiField );
503 
504  QDomElement eficsField = doc.createElement( QStringLiteral( "editforminitcodesource" ) );
505  eficsField.appendChild( doc.createTextNode( QString::number( initCodeSource() ) ) );
506  node.appendChild( eficsField );
507 
508  QDomElement efifpField = doc.createElement( QStringLiteral( "editforminitfilepath" ) );
509  efifpField.appendChild( doc.createTextNode( context.pathResolver().writePath( initFilePath() ) ) );
510  node.appendChild( efifpField );
511 
512  QDomElement eficField = doc.createElement( QStringLiteral( "editforminitcode" ) );
513  eficField.appendChild( doc.createCDATASection( initCode() ) );
514  node.appendChild( eficField );
515 
516  QDomElement fFSuppElem = doc.createElement( QStringLiteral( "featformsuppress" ) );
517  QDomText fFSuppText = doc.createTextNode( QString::number( suppress() ) );
518  fFSuppElem.appendChild( fFSuppText );
519  node.appendChild( fFSuppElem );
520 
521  // tab display
522  QDomElement editorLayoutElem = doc.createElement( QStringLiteral( "editorlayout" ) );
523  switch ( layout() )
524  {
526  editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "uifilelayout" ) ) );
527  break;
528 
530  editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "tablayout" ) ) );
531  break;
532 
534  default:
535  editorLayoutElem.appendChild( doc.createTextNode( QStringLiteral( "generatedlayout" ) ) );
536  break;
537  }
538 
539  node.appendChild( editorLayoutElem );
540 
541  // tabs and groups of edit form
542  if ( !tabs().empty() && d->mConfiguredRootContainer )
543  {
544  QDomElement tabsElem = doc.createElement( QStringLiteral( "attributeEditorForm" ) );
545 
546  QDomElement rootElem = d->mInvisibleRootContainer->toDomElement( doc );
547  QDomNodeList elemList = rootElem.childNodes();
548 
549  while ( !elemList.isEmpty() )
550  {
551  tabsElem.appendChild( elemList.at( 0 ) );
552  }
553 
554  node.appendChild( tabsElem );
555  }
556 
557  QDomElement editableElem = doc.createElement( QStringLiteral( "editable" ) );
558  for ( auto editIt = d->mFieldEditables.constBegin(); editIt != d->mFieldEditables.constEnd(); ++editIt )
559  {
560  QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
561  fieldElem.setAttribute( QStringLiteral( "name" ), editIt.key() );
562  fieldElem.setAttribute( QStringLiteral( "editable" ), editIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
563  editableElem.appendChild( fieldElem );
564  }
565  node.appendChild( editableElem );
566 
567  QDomElement labelOnTopElem = doc.createElement( QStringLiteral( "labelOnTop" ) );
568  for ( auto labelOnTopIt = d->mLabelOnTop.constBegin(); labelOnTopIt != d->mLabelOnTop.constEnd(); ++labelOnTopIt )
569  {
570  QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
571  fieldElem.setAttribute( QStringLiteral( "name" ), labelOnTopIt.key() );
572  fieldElem.setAttribute( QStringLiteral( "labelOnTop" ), labelOnTopIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
573  labelOnTopElem.appendChild( fieldElem );
574  }
575  node.appendChild( labelOnTopElem );
576 
577  // Store data defined field properties
578  QDomElement ddFieldPropsElement = doc.createElement( QStringLiteral( "dataDefinedFieldProperties" ) );
579  for ( auto it = d->mDataDefinedFieldProperties.constBegin(); it != d->mDataDefinedFieldProperties.constEnd(); ++it )
580  {
581  QDomElement ddPropsElement = doc.createElement( QStringLiteral( "field" ) );
582  ddPropsElement.setAttribute( QStringLiteral( "name" ), it.key() );
583  it.value().writeXml( ddPropsElement, propertyDefinitions() );
584  ddFieldPropsElement.appendChild( ddPropsElement );
585  }
586  node.appendChild( ddFieldPropsElement );
587 
588  QDomElement widgetsElem = doc.createElement( QStringLiteral( "widgets" ) );
589 
590  QMap<QString, QVariantMap >::ConstIterator configIt( d->mWidgetConfigs.constBegin() );
591 
592  while ( configIt != d->mWidgetConfigs.constEnd() )
593  {
594  QDomElement widgetElem = doc.createElement( QStringLiteral( "widget" ) );
595  widgetElem.setAttribute( QStringLiteral( "name" ), configIt.key() );
596  // widgetElem.setAttribute( "notNull", );
597 
598  QDomElement configElem = QgsXmlUtils::writeVariant( configIt.value(), doc );
599  configElem.setTagName( QStringLiteral( "config" ) );
600  widgetElem.appendChild( configElem );
601  widgetsElem.appendChild( widgetElem );
602  ++configIt;
603  }
604 
605  node.appendChild( widgetsElem );
606 
608 }
609 
611 {
612  QgsAttributeEditorElement *newElement = nullptr;
613 
614  if ( elem.tagName() == QLatin1String( "attributeEditorContainer" ) )
615  {
616  QColor backgroundColor( elem.attribute( QStringLiteral( "backgroundColor" ), QString() ) );
617  QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ),
618  elem.attribute( QStringLiteral( "name" ) ) ), parent, backgroundColor );
619  bool ok;
620  int cc = elem.attribute( QStringLiteral( "columnCount" ) ).toInt( &ok );
621  if ( !ok )
622  cc = 0;
623  container->setColumnCount( cc );
624 
625  bool isGroupBox = elem.attribute( QStringLiteral( "groupBox" ) ).toInt( &ok );
626  if ( ok )
627  container->setIsGroupBox( isGroupBox );
628  else
629  container->setIsGroupBox( parent );
630 
631  bool visibilityExpressionEnabled = elem.attribute( QStringLiteral( "visibilityExpressionEnabled" ) ).toInt( &ok );
632  QgsOptionalExpression visibilityExpression;
633  if ( ok )
634  {
635  visibilityExpression.setEnabled( visibilityExpressionEnabled );
636  visibilityExpression.setData( QgsExpression( elem.attribute( QStringLiteral( "visibilityExpression" ) ) ) );
637  }
638  container->setVisibilityExpression( visibilityExpression );
639 
640  QDomNodeList childNodeList = elem.childNodes();
641 
642  for ( int i = 0; i < childNodeList.size(); i++ )
643  {
644  QDomElement childElem = childNodeList.at( i ).toElement();
645  QgsAttributeEditorElement *myElem = attributeEditorElementFromDomElement( childElem, container, layerId, context );
646  if ( myElem )
647  container->addChildElement( myElem );
648  }
649 
650  newElement = container;
651  }
652  else if ( elem.tagName() == QLatin1String( "attributeEditorField" ) )
653  {
654  QString name = elem.attribute( QStringLiteral( "name" ) );
655  int idx = d->mFields.lookupField( name );
656  newElement = new QgsAttributeEditorField( name, idx, parent );
657  }
658  else if ( elem.tagName() == QLatin1String( "attributeEditorRelation" ) )
659  {
660  // At this time, the relations are not loaded
661  // So we only grab the id and delegate the rest to onRelationsLoaded()
662  QgsAttributeEditorRelation *relElement = new QgsAttributeEditorRelation( elem.attribute( QStringLiteral( "relation" ), QStringLiteral( "[None]" ) ), parent );
663  if ( elem.hasAttribute( "buttons" ) )
664  {
665  QString buttonString = elem.attribute( QStringLiteral( "buttons" ), qgsFlagValueToKeys( QgsAttributeEditorRelation::Button::AllButtons ) );
666  relElement->setVisibleButtons( qgsFlagKeysToValue( buttonString, QgsAttributeEditorRelation::Button::AllButtons ) );
667  }
668  else
669  {
670  // pre QGIS 3.16 compatibility
671  QgsAttributeEditorRelation::Buttons buttons = QgsAttributeEditorRelation::Button::AllButtons;
672  buttons.setFlag( QgsAttributeEditorRelation::Button::Link, elem.attribute( QStringLiteral( "showLinkButton" ), QStringLiteral( "1" ) ).toInt() );
673  buttons.setFlag( QgsAttributeEditorRelation::Button::Unlink, elem.attribute( QStringLiteral( "showUnlinkButton" ), QStringLiteral( "1" ) ).toInt() );
674  buttons.setFlag( QgsAttributeEditorRelation::Button::SaveChildEdits, elem.attribute( QStringLiteral( "showSaveChildEditsButton" ), QStringLiteral( "1" ) ).toInt() );
675  relElement->setVisibleButtons( buttons );
676  }
677  if ( elem.hasAttribute( QStringLiteral( "forceSuppressFormPopup" ) ) )
678  {
679  relElement->setForceSuppressFormPopup( elem.attribute( QStringLiteral( "forceSuppressFormPopup" ) ).toInt() );
680  }
681  else
682  {
683  // pre QGIS 3.16 compatibility - the widgets section is read before
684  relElement->setForceSuppressFormPopup( widgetConfig( elem.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "force-suppress-popup" ), false ).toBool() );
685  }
686 
687  if ( elem.hasAttribute( QStringLiteral( "nmRelationId" ) ) )
688  {
689  relElement->setNmRelationId( elem.attribute( QStringLiteral( "nmRelationId" ) ) );
690  }
691  else
692  {
693  // pre QGIS 3.16 compatibility - the widgets section is read before
694  relElement->setNmRelationId( widgetConfig( elem.attribute( QStringLiteral( "relation" ) ) ).value( QStringLiteral( "nm-rel" ) ) );
695  }
696  if ( elem.hasAttribute( "label" ) )
697  {
698  QString label = elem.attribute( QStringLiteral( "label" ) );
699  relElement->setLabel( label );
700  }
701  newElement = relElement;
702  }
703  else if ( elem.tagName() == QLatin1String( "attributeEditorQmlElement" ) )
704  {
705  QgsAttributeEditorQmlElement *qmlElement = new QgsAttributeEditorQmlElement( elem.attribute( QStringLiteral( "name" ) ), parent );
706  qmlElement->setQmlCode( elem.text() );
707  newElement = qmlElement;
708  }
709  else if ( elem.tagName() == QLatin1String( "attributeEditorHtmlElement" ) )
710  {
711  QgsAttributeEditorHtmlElement *htmlElement = new QgsAttributeEditorHtmlElement( elem.attribute( QStringLiteral( "name" ) ), parent );
712  htmlElement->setHtmlCode( elem.text() );
713  newElement = htmlElement;
714  }
715 
716  if ( newElement )
717  {
718  if ( elem.hasAttribute( QStringLiteral( "showLabel" ) ) )
719  newElement->setShowLabel( elem.attribute( QStringLiteral( "showLabel" ) ).toInt() );
720  else
721  newElement->setShowLabel( true );
722  }
723 
724  return newElement;
725 }
726 
728 {
729  return mColumnCount;
730 }
731 
733 {
734  mColumnCount = columnCount;
735 }
736 
738 {
740 
741  const auto childElements = children();
742 
743  for ( QgsAttributeEditorElement *child : childElements )
744  {
745  element->addChildElement( child->clone( element ) );
746  }
747  element->mIsGroupBox = mIsGroupBox;
748  element->mColumnCount = mColumnCount;
749  element->mVisibilityExpression = mVisibilityExpression;
750 
751  return element;
752 }
753 
754 void QgsAttributeEditorContainer::saveConfiguration( QDomElement &elem ) const
755 {
756  elem.setAttribute( QStringLiteral( "columnCount" ), mColumnCount );
757  elem.setAttribute( QStringLiteral( "groupBox" ), mIsGroupBox ? 1 : 0 );
758  elem.setAttribute( QStringLiteral( "visibilityExpressionEnabled" ), mVisibilityExpression.enabled() ? 1 : 0 );
759  elem.setAttribute( QStringLiteral( "visibilityExpression" ), mVisibilityExpression->expression() );
760  if ( mBackgroundColor.isValid() )
761  elem.setAttribute( QStringLiteral( "backgroundColor" ), mBackgroundColor.name( ) );
762  const auto constMChildren = mChildren;
763  for ( QgsAttributeEditorElement *child : constMChildren )
764  {
765  QDomDocument doc = elem.ownerDocument();
766  elem.appendChild( child->toDomElement( doc ) );
767  }
768 }
769 
770 QString QgsAttributeEditorContainer::typeIdentifier() const
771 {
772  return QStringLiteral( "attributeEditorContainer" );
773 }
QgsAttributeEditorContainer::setVisibilityExpression
void setVisibilityExpression(const QgsOptionalExpression &visibilityExpression)
The visibility expression is used in the attribute form to show or hide this container based on an ex...
Definition: qgsattributeeditorelement.cpp:35
QgsAttributeEditorElement
This is an abstract base class for any elements of a drag and drop form.
Definition: qgsattributeeditorelement.h:40
QgsProject::relationManager
QgsRelationManager * relationManager
Definition: qgsproject.h:105
QgsEditFormConfig::initCodeSource
PythonInitCodeSource initCodeSource() const
Returns Python code source for edit form initialization (if it shall be loaded from a file,...
Definition: qgseditformconfig.cpp:313
QgsAttributeEditorContainer::~QgsAttributeEditorContainer
~QgsAttributeEditorContainer() override
Definition: qgseditformconfig.cpp:27
qgsFlagValueToKeys
QString qgsFlagValueToKeys(const T &value)
Returns the value for the given keys of a flag.
Definition: qgis.h:549
QgsOptional::enabled
bool enabled() const
Check if this optional is enabled.
Definition: qgsoptional.h:89
QgsAttributeEditorContainer::QgsAttributeEditorContainer
QgsAttributeEditorContainer(const QString &name, QgsAttributeEditorElement *parent, const QColor &backgroundColor=QColor())
Creates a new attribute editor container.
Definition: qgsattributeeditorelement.h:182
QgsEditFormConfig::~QgsEditFormConfig
~QgsEditFormConfig()
Definition: qgseditformconfig.cpp:160
QgsAttributeEditorContainer::columnCount
int columnCount() const
Gets the number of columns in this group.
Definition: qgseditformconfig.cpp:727
QgsEditFormConfig::GeneratedLayout
@ GeneratedLayout
Autogenerate a simple tabular layout for the form.
Definition: qgseditformconfig.h:47
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:35
QgsAttributeEditorElement::AeTypeRelation
@ AeTypeRelation
A relation.
Definition: qgsattributeeditorelement.h:66
QgsEditFormConfig::setInitFunction
void setInitFunction(const QString &function)
Set Python function for edit form initialization.
Definition: qgseditformconfig.cpp:278
QgsAttributeEditorContainer::clone
QgsAttributeEditorElement * clone(QgsAttributeEditorElement *parent) const override
Creates a deep copy of this element.
Definition: qgseditformconfig.cpp:737
QgsEditFormConfig::initFunction
QString initFunction() const
Gets Python function for edit form initialization.
Definition: qgseditformconfig.cpp:273
QgsEditFormConfig::QgsEditFormConfig
QgsEditFormConfig()
Create a new edit form config.
Definition: qgseditformconfig.cpp:32
QgsAttributeEditorField
This element will load a field's widget onto the form.
Definition: qgsattributeeditorelement.h:302
QgsEditFormConfig::PythonInitCodeSource
PythonInitCodeSource
The Python init code source options.
Definition: qgseditformconfig.h:94
QgsEditFormConfig::labelOnTop
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
Definition: qgseditformconfig.cpp:247
qgsreadwritecontext.h
QgsAttributeEditorElement::setShowLabel
void setShowLabel(bool showLabel)
Controls if this element should be labeled with a title (field, relation or groupname).
Definition: qgsattributeeditorelement.cpp:130
QgsAbstractPropertyCollection::readXml
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
Definition: qgspropertycollection.cpp:108
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsEditFormConfig::setLayout
void setLayout(EditorLayout editorLayout)
Sets the active layout style for the attribute editor for this layer.
Definition: qgseditformconfig.cpp:201
qgspathresolver.h
QgsEditFormConfig::setDataDefinedFieldProperties
void setDataDefinedFieldProperties(const QString &fieldName, const QgsPropertyCollection &properties)
Set data defined properties for fieldName to properties.
Definition: qgseditformconfig.cpp:37
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsRelation::id
Q_GADGET QString id
Definition: qgsrelation.h:45
QgsNetworkContentFetcherRegistry::DownloadImmediately
@ DownloadImmediately
The download will start immediately, not need to run QgsFecthedContent::download()
Definition: qgsnetworkcontentfetcherregistry.h:131
QgsApplication::instance
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
Definition: qgsapplication.cpp:411
QgsEditFormConfig::initFilePath
QString initFilePath() const
Gets Python external file path for edit form initialization.
Definition: qgseditformconfig.cpp:295
field
const QgsField & field
Definition: qgsfield.h:456
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsAttributeEditorContainer::children
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
Definition: qgsattributeeditorelement.h:218
QgsAttributeEditorRelation::setVisibleButtons
void setVisibleButtons(const QgsAttributeEditorRelation::Buttons &buttons)
Defines the buttons which are shown.
Definition: qgsattributeeditorelement.cpp:179
QgsReadWriteContext::enterCategory
MAYBE_UNUSED NODISCARD QgsReadWriteContextCategoryPopper enterCategory(const QString &category, const QString &details=QString())
Push a category to the stack.
Definition: qgsreadwritecontext.cpp:62
QgsEditFormConfig::addTab
void addTab(QgsAttributeEditorElement *data)
Adds a new element to the invisible root container in the layout.
Definition: qgseditformconfig.cpp:174
QgsEditFormConfig::FeatureFormSuppress
FeatureFormSuppress
Types of feature form suppression after feature creation.
Definition: qgseditformconfig.h:83
QgsAttributeEditorRelation::init
bool init(QgsRelationManager *relManager)
Initializes the relation from the id.
Definition: qgsattributeeditorelement.cpp:89
QgsEditFormConfig::invisibleRootContainer
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
Definition: qgseditformconfig.cpp:191
qgsapplication.h
qgsnetworkcontentfetcherregistry.h
QgsEditFormConfig::setInitFilePath
void setInitFilePath(const QString &filePath)
Set Python external file path for edit form initialization.
Definition: qgseditformconfig.cpp:300
QgsAttributeEditorRelation::setNmRelationId
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
Definition: qgsattributeeditorelement.cpp:194
QgsPropertiesDefinition
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
Definition: qgspropertycollection.h:29
QgsPathResolver::writePath
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
Definition: qgspathresolver.cpp:192
QgsEditFormConfig::setInitCode
void setInitCode(const QString &code)
Set Python code for edit form initialization.
Definition: qgseditformconfig.cpp:289
QgsReadWriteContextCategoryPopper
Allows entering a context category and takes care of leaving this category on deletion of the class.
Definition: qgsreadwritecontext.h:159
QgsAttributeEditorElement::AeTypeContainer
@ AeTypeContainer
A container.
Definition: qgsattributeeditorelement.h:64
QgsReadWriteContext::projectTranslator
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
Definition: qgsreadwritecontext.h:108
QgsAttributeEditorQmlElement
An attribute editor widget that will represent arbitrary QML code.
Definition: qgsattributeeditorelement.h:531
QgsEditFormConfig::operator=
QgsEditFormConfig & operator=(const QgsEditFormConfig &o)
Definition: qgseditformconfig.cpp:163
QgsEditFormConfig::CodeSourceDialog
@ CodeSourceDialog
Use the Python code provided in the dialog.
Definition: qgseditformconfig.h:97
QgsNetworkContentFetcherRegistry::fetch
const QgsFetchedContent * fetch(const QString &url, FetchingMode fetchingMode=DownloadLater)
Initialize a download for the given URL.
Definition: qgsnetworkcontentfetcherregistry.cpp:33
QgsXmlUtils::readVariant
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
Definition: qgsxmlutils.cpp:251
QgsAttributeEditorRelation
This element will load a relation editor onto the form.
Definition: qgsattributeeditorelement.h:335
QgsEditFormConfig::setReadOnly
void setReadOnly(int idx, bool readOnly=true)
If set to false, the widget at the given index will be read-only.
Definition: qgseditformconfig.cpp:255
QgsEditFormConfig::widgetConfig
QVariantMap widgetConfig(const QString &widgetName) const
Gets the configuration for the editor widget with the given name.
Definition: qgseditformconfig.cpp:53
QgsOptional::setData
void setData(const T &data)
Set the payload data.
Definition: qgsoptional.h:129
QgsEditFormConfig::writeXml
void writeXml(QDomNode &node, const QgsReadWriteContext &context) const
Write XML information Serialize on project save.
Definition: qgseditformconfig.cpp:489
QgsAttributeEditorQmlElement::setQmlCode
void setQmlCode(const QString &qmlCode)
Sets the QML code that will be represented within this widget to qmlCode.
Definition: qgsattributeeditorelement.cpp:227
QgsEditFormConfig::SuppressDefault
@ SuppressDefault
Use the application-wide setting.
Definition: qgseditformconfig.h:84
QgsEditFormConfig::operator==
bool operator==(const QgsEditFormConfig &o)
Definition: qgseditformconfig.cpp:169
QgsAttributeEditorRelation::relation
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
Definition: qgsattributeeditorelement.h:404
QgsEditFormConfig::setUiForm
void setUiForm(const QString &ui)
Set path to the .ui form.
Definition: qgseditformconfig.cpp:215
QgsAttributeEditorHtmlElement::setHtmlCode
void setHtmlCode(const QString &htmlCode)
Sets the HTML code that will be represented within this widget to htmlCode.
Definition: qgsattributeeditorelement.cpp:256
QgsEditFormConfig::TabLayout
@ TabLayout
Use a layout with tabs and group boxes. Needs to be configured.
Definition: qgseditformconfig.h:48
QgsAttributeEditorContainer::setColumnCount
void setColumnCount(int columnCount)
Set the number of columns in this group.
Definition: qgseditformconfig.cpp:732
QgsOptionalExpression
An expression with an additional enabled flag.
Definition: qgsoptionalexpression.h:40
qgsrelationmanager.h
QgsEditFormConfig::setLabelOnTop
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...
Definition: qgseditformconfig.cpp:264
QgsEditFormConfig::readOnly
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 ...
Definition: qgseditformconfig.cpp:234
QgsEditFormConfig
Definition: qgseditformconfig.h:38
QgsAttributeEditorContainer::setIsGroupBox
virtual void setIsGroupBox(bool isGroupBox)
Determines if this container is rendered as collapsible group box or tab in a tabwidget.
Definition: qgsattributeeditorelement.h:204
qgsxmlutils.h
QgsEditFormConfig::setWidgetConfig
bool setWidgetConfig(const QString &widgetName, const QVariantMap &config)
Set the editor widget config for a widget which is not for a simple field.
Definition: qgseditformconfig.cpp:129
qgseditformconfig.h
QgsPropertyCollection
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Definition: qgspropertycollection.h:319
QgsPathResolver::readPath
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Definition: qgspathresolver.cpp:35
QgsAttributeEditorRelation::setForceSuppressFormPopup
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status to forceSuppressFormPopup.
Definition: qgsattributeeditorelement.cpp:184
QgsFields::OriginExpression
@ OriginExpression
Field is calculated from an expression.
Definition: qgsfields.h:54
QgsEditFormConfig::removeWidgetConfig
bool removeWidgetConfig(const QString &widgetName)
Remove the configuration for the editor widget with the given name.
Definition: qgseditformconfig.cpp:149
QgsOptional::setEnabled
void setEnabled(bool enabled)
Set if this optional is enabled.
Definition: qgsoptional.h:99
QgsEditFormConfig::clearTabs
void clearTabs()
Clears all the tabs for the attribute editor form with EditorLayout::TabLayout.
Definition: qgseditformconfig.cpp:185
QgsAttributeEditorContainer
This is a container for attribute editors, used to group them visually in the attribute form if it is...
Definition: qgsattributeeditorelement.h:172
QgsEditFormConfig::layout
EditorLayout layout() const
Gets the active layout style for the attribute editor for this layer.
Definition: qgseditformconfig.cpp:196
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsEditFormConfig::dataDefinedFieldProperties
QgsPropertyCollection dataDefinedFieldProperties(const QString &fieldName) const
Returns data defined properties for fieldName.
Definition: qgseditformconfig.cpp:43
QgsEditFormConfig::uiForm
QString uiForm() const
Returns the path or URL to the .ui form.
Definition: qgseditformconfig.cpp:210
QgsAttributeEditorElement::name
QString name() const
Returns the name of this element.
Definition: qgsattributeeditorelement.h:93
qgseditformconfig_p.h
qgslogger.h
QgsEditFormConfig::propertyDefinitions
static const QgsPropertiesDefinition & propertyDefinitions()
Returns data defined property definitions.
Definition: qgseditformconfig.cpp:48
QgsEditFormConfig::setSuppress
void setSuppress(FeatureFormSuppress s)
Sets type of feature form pop-up suppression after feature creation (overrides app setting)
Definition: qgseditformconfig.cpp:329
QgsAttributeEditorRelation::setLabel
void setLabel(const QString &label=QString())
Sets label for this element If it's empty it takes the relation id as label.
Definition: qgsattributeeditorelement.cpp:204
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:105
QgsEditFormConfig::tabs
QList< QgsAttributeEditorElement * > tabs() const
Returns a list of tabs for EditorLayout::TabLayout obtained from the invisible root container.
Definition: qgseditformconfig.cpp:180
QgsEditFormConfig::attributeEditorElementFromDomElement
QgsAttributeEditorElement * attributeEditorElementFromDomElement(QDomElement &elem, QgsAttributeEditorElement *parent, const QString &layerId=QString(), const QgsReadWriteContext &context=QgsReadWriteContext())
Deserialize drag and drop designer elements.
Definition: qgseditformconfig.cpp:610
QgsXmlUtils::writeVariant
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
Definition: qgsxmlutils.cpp:106
qgsFlagKeysToValue
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue)
Returns the value corresponding to the given keys of a flag.
Definition: qgis.h:561
QgsEditFormConfig::EditorLayout
EditorLayout
The different types to layout the attribute editor.
Definition: qgseditformconfig.h:46
QgsEditFormConfig::initCode
QString initCode() const
Gets Python code for edit form initialization.
Definition: qgseditformconfig.cpp:284
QgsEditFormConfig::readXml
void readXml(const QDomNode &node, QgsReadWriteContext &context)
Read XML information Deserialize on project load.
Definition: qgseditformconfig.cpp:335
QgsApplication::networkContentFetcherRegistry
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
Definition: qgsapplication.cpp:2193
qgsproject.h
QgsEditFormConfig::suppress
FeatureFormSuppress suppress() const
Type of feature form pop-up suppression after feature creation (overrides app setting)
Definition: qgseditformconfig.cpp:324
QgsFields::OriginJoin
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
QgsEditFormConfig::setInitCodeSource
void setInitCodeSource(PythonInitCodeSource initCodeSource)
Sets if Python code shall be used for edit form initialization and its origin.
Definition: qgseditformconfig.cpp:318
QgsAttributeEditorContainer::addChildElement
virtual void addChildElement(QgsAttributeEditorElement *element)
Add a child element to this container.
Definition: qgsattributeeditorelement.cpp:20
QgsProjectTranslator::translate
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
The derived translate() translates with QTranslator and qm file the sourceText.
QgsReadWriteContext::pathResolver
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
Definition: qgsreadwritecontext.cpp:47
qgsmessagelog.h
QgsAttributeEditorHtmlElement
An attribute editor widget that will represent arbitrary HTML code.
Definition: qgsattributeeditorelement.h:572
QgsAttributeEditorElement::parent
QgsAttributeEditorElement * parent() const
Gets the parent of this element.
Definition: qgsattributeeditorelement.h:107
QgsEditFormConfig::UiFileLayout
@ UiFileLayout
Load a .ui file for the layout. Needs to be configured.
Definition: qgseditformconfig.h:49