QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsproject.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsproject.cpp - description
3  -------------------
4  begin : July 23, 2004
5  copyright : (C) 2004 by Mark Coletti
6  email : mcoletti at gmail.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsproject.h"
19 
20 #include "qgsdatasourceuri.h"
22 #include "qgslayertree.h"
23 #include "qgslayertreeutils.h"
25 #include "qgslogger.h"
26 #include "qgsmessagelog.h"
27 #include "qgspluginlayer.h"
28 #include "qgspluginlayerregistry.h"
30 #include "qgssnappingconfig.h"
31 #include "qgspathresolver.h"
32 #include "qgsprojectstorage.h"
34 #include "qgsprojectversion.h"
35 #include "qgsrasterlayer.h"
36 #include "qgsreadwritecontext.h"
37 #include "qgsrectangle.h"
38 #include "qgsrelationmanager.h"
39 #include "qgsannotationmanager.h"
40 #include "qgsvectorlayerjoininfo.h"
41 #include "qgsmapthemecollection.h"
42 #include "qgslayerdefinition.h"
43 #include "qgsunittypes.h"
44 #include "qgstransaction.h"
45 #include "qgstransactiongroup.h"
46 #include "qgsvectordataprovider.h"
48 #include "qgssettings.h"
49 #include "qgsmaplayerlistutils.h"
50 #include "qgsmeshlayer.h"
51 #include "qgslayoutmanager.h"
52 #include "qgsmaplayerstore.h"
53 #include "qgsziputils.h"
54 #include "qgsauxiliarystorage.h"
55 
56 #include <QApplication>
57 #include <QFileInfo>
58 #include <QDomNode>
59 #include <QObject>
60 #include <QTextStream>
61 #include <QTemporaryFile>
62 #include <QDir>
63 #include <QUrl>
64 
65 
66 #ifdef _MSC_VER
67 #include <sys/utime.h>
68 #else
69 #include <utime.h>
70 #endif
71 
72 // canonical project instance
73 QgsProject *QgsProject::sProject = nullptr;
74 
83 QStringList makeKeyTokens_( const QString &scope, const QString &key )
84 {
85  QStringList keyTokens = QStringList( scope );
86  keyTokens += key.split( '/', QString::SkipEmptyParts );
87 
88  // be sure to include the canonical root node
89  keyTokens.push_front( QStringLiteral( "properties" ) );
90 
91  //check validy of keys since an unvalid xml name will will be dropped upon saving the xml file. If not valid, we print a message to the console.
92  for ( int i = 0; i < keyTokens.size(); ++i )
93  {
94  QString keyToken = keyTokens.at( i );
95 
96  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
97  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
98  QString nameCharRegexp = QStringLiteral( "[^:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x2FF\\x370-\\x37D\\x37F-\\x1FFF\\x200C-\\x200D\\x2070-\\x218F\\x2C00-\\x2FEF\\x3001-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFFD\\-\\.0-9\\xB7\\x0300-\\x036F\\x203F-\\x2040]" );
99  QString nameStartCharRegexp = QStringLiteral( "^[^:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x2FF\\x370-\\x37D\\x37F-\\x1FFF\\x200C-\\x200D\\x2070-\\x218F\\x2C00-\\x2FEF\\x3001-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFFD]" );
100 
101  if ( keyToken.contains( QRegExp( nameCharRegexp ) ) || keyToken.contains( QRegExp( nameStartCharRegexp ) ) )
102  {
103 
104  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
105  QgsMessageLog::logMessage( errorString, QString(), Qgis::Critical );
106 
107  }
108 
109  }
110 
111  return keyTokens;
112 }
113 
114 
115 
125 QgsProjectProperty *findKey_( const QString &scope,
126  const QString &key,
127  QgsProjectPropertyKey &rootProperty )
128 {
129  QgsProjectPropertyKey *currentProperty = &rootProperty;
130  QgsProjectProperty *nextProperty; // link to next property down hierarchy
131 
132  QStringList keySequence = makeKeyTokens_( scope, key );
133 
134  while ( !keySequence.isEmpty() )
135  {
136  // if the current head of the sequence list matches the property name,
137  // then traverse down the property hierarchy
138  if ( keySequence.first() == currentProperty->name() )
139  {
140  // remove front key since we're traversing down a level
141  keySequence.pop_front();
142 
143  if ( 1 == keySequence.count() )
144  {
145  // if we have only one key name left, then return the key found
146  return currentProperty->find( keySequence.front() );
147  }
148  else if ( keySequence.isEmpty() )
149  {
150  // if we're out of keys then the current property is the one we
151  // want; i.e., we're in the rate case of being at the top-most
152  // property node
153  return currentProperty;
154  }
155  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
156  {
157  if ( nextProperty->isKey() )
158  {
159  currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
160  }
161  else if ( nextProperty->isValue() && 1 == keySequence.count() )
162  {
163  // it may be that this may be one of several property value
164  // nodes keyed by QDict string; if this is the last remaining
165  // key token and the next property is a value node, then
166  // that's the situation, so return the currentProperty
167  return currentProperty;
168  }
169  else
170  {
171  // QgsProjectPropertyValue not Key, so return null
172  return nullptr;
173  }
174  }
175  else
176  {
177  // if the next key down isn't found
178  // then the overall key sequence doesn't exist
179  return nullptr;
180  }
181  }
182  else
183  {
184  return nullptr;
185  }
186  }
187 
188  return nullptr;
189 }
190 
191 
192 
202 QgsProjectProperty *addKey_( const QString &scope,
203  const QString &key,
204  QgsProjectPropertyKey *rootProperty,
205  const QVariant &value,
206  bool &propertiesModified )
207 {
208  QStringList keySequence = makeKeyTokens_( scope, key );
209 
210  // cursor through property key/value hierarchy
211  QgsProjectPropertyKey *currentProperty = rootProperty;
212  QgsProjectProperty *nextProperty; // link to next property down hierarchy
213  QgsProjectPropertyKey *newPropertyKey = nullptr;
214 
215  propertiesModified = false;
216  while ( ! keySequence.isEmpty() )
217  {
218  // if the current head of the sequence list matches the property name,
219  // then traverse down the property hierarchy
220  if ( keySequence.first() == currentProperty->name() )
221  {
222  // remove front key since we're traversing down a level
223  keySequence.pop_front();
224 
225  // if key sequence has one last element, then we use that as the
226  // name to store the value
227  if ( 1 == keySequence.count() )
228  {
229  QgsProjectProperty *property = currentProperty->find( keySequence.front() );
230  if ( !property || property->value() != value )
231  {
232  currentProperty->setValue( keySequence.front(), value );
233  propertiesModified = true;
234  }
235 
236  return currentProperty;
237  }
238  // we're at the top element if popping the keySequence element
239  // will leave it empty; in that case, just add the key
240  else if ( keySequence.isEmpty() )
241  {
242  if ( currentProperty->value() != value )
243  {
244  currentProperty->setValue( value );
245  propertiesModified = true;
246  }
247 
248  return currentProperty;
249  }
250  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
251  {
252  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
253 
254  if ( currentProperty )
255  {
256  continue;
257  }
258  else // QgsProjectPropertyValue not Key, so return null
259  {
260  return nullptr;
261  }
262  }
263  else // the next subkey doesn't exist, so add it
264  {
265  if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
266  {
267  currentProperty = newPropertyKey;
268  }
269  continue;
270  }
271  }
272  else
273  {
274  return nullptr;
275  }
276  }
277 
278  return nullptr;
279 }
280 
289 void removeKey_( const QString &scope,
290  const QString &key,
291  QgsProjectPropertyKey &rootProperty )
292 {
293  QgsProjectPropertyKey *currentProperty = &rootProperty;
294 
295  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
296  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
297 
298  QStringList keySequence = makeKeyTokens_( scope, key );
299 
300  while ( ! keySequence.isEmpty() )
301  {
302  // if the current head of the sequence list matches the property name,
303  // then traverse down the property hierarchy
304  if ( keySequence.first() == currentProperty->name() )
305  {
306  // remove front key since we're traversing down a level
307  keySequence.pop_front();
308 
309  // if we have only one key name left, then try to remove the key
310  // with that name
311  if ( 1 == keySequence.count() )
312  {
313  currentProperty->removeKey( keySequence.front() );
314  }
315  // if we're out of keys then the current property is the one we
316  // want to remove, but we can't delete it directly; we need to
317  // delete it from the parent property key container
318  else if ( keySequence.isEmpty() )
319  {
320  previousQgsPropertyKey->removeKey( currentProperty->name() );
321  }
322  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
323  {
324  previousQgsPropertyKey = currentProperty;
325  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
326 
327  if ( currentProperty )
328  {
329  continue;
330  }
331  else // QgsProjectPropertyValue not Key, so return null
332  {
333  return;
334  }
335  }
336  else // if the next key down isn't found
337  {
338  // then the overall key sequence doesn't exist
339  return;
340  }
341  }
342  else
343  {
344  return;
345  }
346  }
347 }
348 
349 QgsProject::QgsProject( QObject *parent )
350  : QObject( parent )
351  , mLayerStore( new QgsMapLayerStore( this ) )
352  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
353  , mSnappingConfig( this )
354  , mRelationManager( new QgsRelationManager( this ) )
355  , mAnnotationManager( new QgsAnnotationManager( this ) )
356  , mLayoutManager( new QgsLayoutManager( this ) )
357  , mRootGroup( new QgsLayerTree )
358  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
359  , mArchive( new QgsProjectArchive() )
360  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
361 {
362  mProperties.setName( QStringLiteral( "properties" ) );
363  clear();
364 
365  // bind the layer tree to the map layer registry.
366  // whenever layers are added to or removed from the registry,
367  // layer tree will be updated
368  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
369  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
370  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
371  connect( this, static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *> & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
372 
373  // proxy map layer store signals to this
374  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersWillBeRemoved ),
375  this, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ) );
376  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QList<QgsMapLayer *> & )>( &QgsMapLayerStore::layersWillBeRemoved ),
377  this, static_cast<void ( QgsProject::* )( const QList<QgsMapLayer *> & )>( &QgsProject::layersWillBeRemoved ) );
378  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QString & )>( &QgsMapLayerStore::layerWillBeRemoved ),
379  this, static_cast<void ( QgsProject::* )( const QString & )>( &QgsProject::layerWillBeRemoved ) );
380  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( QgsMapLayer * )>( &QgsMapLayerStore::layerWillBeRemoved ),
381  this, static_cast<void ( QgsProject::* )( QgsMapLayer * )>( &QgsProject::layerWillBeRemoved ) );
382  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersRemoved ), this, &QgsProject::layersRemoved );
383  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this, &QgsProject::layerRemoved );
384  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this, &QgsProject::removeAll );
385  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this, &QgsProject::layersAdded );
386  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this, &QgsProject::layerWasAdded );
387  if ( QgsApplication::instance() )
389 }
390 
391 
393 {
394  clear();
395  delete mBadLayerHandler;
396  delete mRelationManager;
397  delete mLayerTreeRegistryBridge;
398  delete mRootGroup;
399  if ( this == sProject )
400  {
401  sProject = nullptr;
402  }
403 }
404 
405 void QgsProject::setInstance( QgsProject *project )
406 {
407  sProject = project;
408 }
409 
410 
412 {
413  if ( !sProject )
414  {
415  sProject = new QgsProject;
416  }
417  return sProject;
418 }
419 
420 void QgsProject::setTitle( const QString &title )
421 {
422  if ( title == mMetadata.title() )
423  return;
424 
425  mMetadata.setTitle( title );
426  emit metadataChanged();
427 
428  setDirty( true );
429 }
430 
431 QString QgsProject::title() const
432 {
433  return mMetadata.title();
434 }
435 
437 {
438  return mDirty;
439 }
440 
441 void QgsProject::setDirty( const bool dirty )
442 {
443  if ( dirty && mDirtyBlockCount > 0 )
444  return;
445 
446  if ( mDirty == dirty )
447  return;
448 
449  mDirty = dirty;
450  emit isDirtyChanged( mDirty );
451 }
452 
453 void QgsProject::setPresetHomePath( const QString &path )
454 {
455  if ( path == mHomePath )
456  return;
457 
458  mHomePath = path;
459  emit homePathChanged();
460 
461  setDirty( true );
462 }
463 
464 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
465 {
466  const QList<QgsAttributeEditorElement *> elements = parent->children();
467 
468  for ( QgsAttributeEditorElement *element : elements )
469  {
470  if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
471  {
472  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
473 
474  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
475 
476  if ( !container->children().empty() )
477  registerTranslatableContainers( translationContext, container, layerId );
478  }
479  }
480 }
481 
483 {
484  //register layers
485  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
486 
487  for ( const QgsLayerTreeLayer *layer : layers )
488  {
489  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
490 
491  QgsMapLayer *mapLayer = layer->layer();
492  if ( mapLayer && mapLayer->type() == QgsMapLayer::VectorLayer )
493  {
494  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
495 
496  //register aliases and fields
497  const QgsFields fields = vlayer->fields();
498  for ( const QgsField &field : fields )
499  {
500  QString fieldName;
501  if ( field.alias().isEmpty() )
502  fieldName = field.name();
503  else
504  fieldName = field.alias();
505 
506  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
507 
508  if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueRelation" ) )
509  {
510  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
511  }
512  }
513 
514  //register formcontainers
515  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
516 
517  }
518  }
519 
520  //register layergroups
521  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
522  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
523  {
524  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
525  }
526 
527  //register relations
528  const QList<QgsRelation> &relations = mRelationManager->relations().values();
529  for ( const QgsRelation &relation : relations )
530  {
531  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
532  }
533 }
534 
535 void QgsProject::setFileName( const QString &name )
536 {
537  if ( name == mFile.fileName() )
538  return;
539 
540  QString oldHomePath = homePath();
541 
542  mFile.setFileName( name );
543  emit fileNameChanged();
544 
545  QString newHomePath = homePath();
546  if ( newHomePath != oldHomePath )
547  emit homePathChanged();
548 
549  setDirty( true );
550 }
551 
552 QString QgsProject::fileName() const
553 {
554  return mFile.fileName();
555 }
556 
557 QFileInfo QgsProject::fileInfo() const
558 {
559  return QFileInfo( mFile );
560 }
561 
563 {
565 }
566 
567 QDateTime QgsProject::lastModified() const
568 {
569  if ( QgsProjectStorage *storage = projectStorage() )
570  {
572  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
573  return metadata.lastModified;
574  }
575  else
576  {
577  return QFileInfo( mFile.fileName() ).lastModified();
578  }
579 }
580 
582 {
583  if ( projectStorage() )
584  return QString();
585 
586  if ( mFile.fileName().isEmpty() )
587  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
588 
589  return QFileInfo( mFile.fileName() ).absolutePath();
590 }
591 
593 {
594  if ( projectStorage() )
595  return QString();
596 
597  if ( mFile.fileName().isEmpty() )
598  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
599 
600  return QFileInfo( mFile.fileName() ).absoluteFilePath();
601 }
602 
603 QString QgsProject::baseName() const
604 {
605  if ( QgsProjectStorage *storage = projectStorage() )
606  {
608  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
609  return metadata.name;
610  }
611  else
612  {
613  return QFileInfo( mFile.fileName() ).completeBaseName();
614  }
615 }
616 
618 {
619  return mCrs;
620 }
621 
623 {
624  if ( crs == mCrs )
625  return;
626 
627  mCrs = crs;
628  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
629  setDirty( true );
630  emit crsChanged();
631 }
632 
633 QString QgsProject::ellipsoid() const
634 {
635  if ( !crs().isValid() )
636  return GEO_NONE;
637 
638  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), GEO_NONE );
639 }
640 
641 void QgsProject::setEllipsoid( const QString &ellipsoid )
642 {
643  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
644  emit ellipsoidChanged( ellipsoid );
645 }
646 
648 {
649  return mTransformContext;
650 }
651 
653 {
654  if ( context == mTransformContext )
655  return;
656 
657  mTransformContext = context;
659 }
660 
662 {
663  QgsSettings s;
664 
665  mFile.setFileName( QString() );
666  mProperties.clearKeys();
667  mHomePath.clear();
668  mAutoTransaction = false;
669  mEvaluateDefaultValues = false;
670  mDirty = false;
671  mTrustLayerMetadata = false;
672  mCustomVariables.clear();
673  mMetadata = QgsProjectMetadata();
674  if ( !s.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
675  {
676  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
678  }
679  emit metadataChanged();
680 
682  context.readSettings();
683  setTransformContext( context );
684 
685  mEmbeddedLayers.clear();
686  mRelationManager->clear();
687  mAnnotationManager->clear();
688  mLayoutManager->clear();
689  mSnappingConfig.reset();
690  emit snappingConfigChanged( mSnappingConfig );
692 
693  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
695 
696  mLabelingEngineSettings->clear();
697 
698  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
699  mArchive->clear();
700 
702 
703  // reset some default project properties
704  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
705  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
706  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
707  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
708 
709  //copy default units to project
710  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
711  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
712 
714  mRootGroup->clear();
715 
716  setDirty( false );
717  emit cleared();
718 }
719 
720 // basically a debugging tool to dump property list values
721 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
722 {
723  QgsDebugMsg( QStringLiteral( "current properties:" ) );
724  topQgsPropertyKey.dump();
725 }
726 
727 
758 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
759 {
760  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
761 
762  if ( propertiesElem.isNull() ) // no properties found, so we're done
763  {
764  return;
765  }
766 
767  QDomNodeList scopes = propertiesElem.childNodes();
768 
769  if ( scopes.count() < 1 )
770  {
771  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
772  return;
773  }
774 
775  if ( ! project_properties.readXml( propertiesElem ) )
776  {
777  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
778  }
779 }
780 
781 
786 static void _getTitle( const QDomDocument &doc, QString &title )
787 {
788  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
789 
790  title.clear(); // by default the title will be empty
791 
792  if ( !nl.count() )
793  {
794  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
795  return;
796  }
797 
798  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element OK
799 
800  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
801  {
802  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
803  return;
804  }
805 
806  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
807 
808  if ( !titleTextNode.isText() )
809  {
810  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
811  return;
812  }
813 
814  QDomText titleText = titleTextNode.toText();
815 
816  title = titleText.data();
817 
818 }
819 
820 QgsProjectVersion getVersion( const QDomDocument &doc )
821 {
822  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
823 
824  if ( !nl.count() )
825  {
826  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
827  return QgsProjectVersion( 0, 0, 0, QString() );
828  }
829 
830  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
831 
832  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
833  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
834  return projectVersion;
835 }
836 
837 
839 {
840  return mSnappingConfig;
841 }
842 
844 {
845  if ( mSnappingConfig == snappingConfig )
846  return;
847 
848  mSnappingConfig = snappingConfig;
849  setDirty( true );
850  emit snappingConfigChanged( mSnappingConfig );
851 }
852 
853 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes )
854 {
855  // Layer order is set by the restoring the legend settings from project file.
856  // This is done on the 'readProject( ... )' signal
857 
858  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
859 
860  // process the map layer nodes
861 
862  if ( 0 == nl.count() ) // if we have no layers to process, bail
863  {
864  return true; // Decided to return "true" since it's
865  // possible for there to be a project with no
866  // layers; but also, more imporantly, this
867  // would cause the tests/qgsproject to fail
868  // since the test suite doesn't currently
869  // support test layers
870  }
871 
872  bool returnStatus = true;
873 
874  emit layerLoaded( 0, nl.count() );
875 
876  // order layers based on their dependencies
877  QgsLayerDefinition::DependencySorter depSorter( doc );
878  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
879  return false;
880 
881  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
882 
883  int i = 0;
884  Q_FOREACH ( const QDomNode &node, sortedLayerNodes )
885  {
886  QDomElement element = node.toElement();
887 
888  QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
889  if ( !name.isNull() )
890  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
891 
892  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
893  {
894  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes );
895  }
896  else
897  {
898  QgsReadWriteContext context;
899  context.setPathResolver( pathResolver() );
900  context.setProjectTranslator( this );
901  if ( !addLayer( element, brokenNodes, context ) )
902  {
903  returnStatus = false;
904  }
905  const auto messages = context.takeMessages();
906  if ( messages.count() )
907  {
908  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
909  }
910  }
911  emit layerLoaded( i + 1, nl.count() );
912  i++;
913  }
914 
915  return returnStatus;
916 }
917 
918 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context )
919 {
920  QString type = layerElem.attribute( QStringLiteral( "type" ) );
921  QgsDebugMsgLevel( "Layer type is " + type, 4 );
922  QgsMapLayer *mapLayer = nullptr;
923 
924  if ( type == QLatin1String( "vector" ) )
925  {
926  mapLayer = new QgsVectorLayer;
927 
928  // apply specific settings to vector layer
929  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer ) )
930  {
931  vl->setReadExtentFromXml( mTrustLayerMetadata );
932  }
933  }
934  else if ( type == QLatin1String( "raster" ) )
935  {
936  mapLayer = new QgsRasterLayer;
937  }
938  else if ( type == QLatin1String( "mesh" ) )
939  {
940  mapLayer = new QgsMeshLayer;
941  }
942  else if ( type == QLatin1String( "plugin" ) )
943  {
944  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
945  mapLayer = QgsApplication::pluginLayerRegistry()->createLayer( typeName );
946  }
947 
948  if ( !mapLayer )
949  {
950  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
951 
952  return false;
953  }
954 
955  Q_CHECK_PTR( mapLayer ); // NOLINT
956 
957  // have the layer restore state that is stored in Dom node
958  if ( mapLayer->readLayerXml( layerElem, context ) && mapLayer->isValid() )
959  {
960  emit readMapLayer( mapLayer, layerElem );
961 
962  QList<QgsMapLayer *> myLayers;
963  myLayers << mapLayer;
964  addMapLayers( myLayers );
965 
966  return true;
967  }
968  else
969  {
970  delete mapLayer;
971 
972  QgsDebugMsg( "Unable to load " + type + " layer" );
973  brokenNodes.push_back( layerElem );
974  return false;
975  }
976 }
977 
978 bool QgsProject::read( const QString &filename )
979 {
980  mFile.setFileName( filename );
981 
982  return read();
983 }
984 
986 {
987  QString filename = mFile.fileName();
988  bool rc;
989 
990  if ( QgsProjectStorage *storage = projectStorage() )
991  {
992  QTemporaryFile inDevice;
993  if ( !inDevice.open() )
994  {
995  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
996  return false;
997  }
998 
999  QgsReadWriteContext context;
1000  context.setProjectTranslator( this );
1001  if ( !storage->readProject( filename, &inDevice, context ) )
1002  {
1003  QString err = tr( "Unable to open %1" ).arg( filename );
1004  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1005  if ( !messages.isEmpty() )
1006  err += QStringLiteral( "\n\n" ) + messages.last().message();
1007  setError( err );
1008  return false;
1009  }
1010 
1011  return unzip( inDevice.fileName() ); // calls setError() if returning false
1012  }
1013 
1014  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1015  {
1016  rc = unzip( mFile.fileName() );
1017  }
1018  else
1019  {
1020  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1021  rc = readProjectFile( mFile.fileName() );
1022  }
1023 
1024  //on translation we should not change the filename back
1025  if ( !mTranslator )
1026  {
1027  mFile.setFileName( filename );
1028  }
1029  else
1030  {
1031  //but delete the translator
1032  mTranslator.reset( nullptr );
1033  }
1034 
1035  return rc;
1036 }
1037 
1038 bool QgsProject::readProjectFile( const QString &filename )
1039 {
1040  QFile projectFile( filename );
1041  clearError();
1042 
1043  QgsSettings settings;
1044 
1045  QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), settings.value( QStringLiteral( "locale/userLocale" ), QString() ).toString() );
1046 
1047  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1048  {
1049  mTranslator.reset( new QTranslator() );
1050  mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1051  }
1052 
1053  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1054 
1055  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1056  {
1057  projectFile.close();
1058 
1059  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1060 
1061  return false;
1062  }
1063 
1064  // location of problem associated with errorMsg
1065  int line, column;
1066  QString errorMsg;
1067 
1068  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1069  {
1070  // want to make this class as GUI independent as possible; so commented out
1071 #if 0
1072  QMessageBox::critical( 0, tr( "Read Project File" ),
1073  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
1074 #endif
1075 
1076  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1077  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1078 
1079  QgsDebugMsg( errorString );
1080 
1081  projectFile.close();
1082 
1083  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1084 
1085  return false;
1086  }
1087 
1088  projectFile.close();
1089 
1090  QgsDebugMsg( "Opened document " + projectFile.fileName() );
1091 
1092  // get project version string, if any
1093  QgsProjectVersion fileVersion = getVersion( *doc );
1094  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
1095 
1096  if ( thisVersion > fileVersion )
1097  {
1098  QgsLogger::warning( "Loading a file that was saved with an older "
1099  "version of qgis (saved in " + fileVersion.text() +
1100  ", loaded in " + Qgis::QGIS_VERSION +
1101  "). Problems may occur." );
1102 
1103  QgsProjectFileTransform projectFile( *doc, fileVersion );
1104 
1105  // Shows a warning when an old project file is read.
1106  emit oldProjectVersionWarning( fileVersion.text() );
1107  QgsDebugMsg( QStringLiteral( "Emitting oldProjectVersionWarning(oldVersion)." ) );
1108 
1109  projectFile.updateRevision( thisVersion );
1110  }
1111 
1112  // start new project, just keep the file name and auxiliary storage
1113  QString fileName = mFile.fileName();
1114  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1115  clear();
1116  mAuxiliaryStorage = std::move( aStorage );
1117  mFile.setFileName( fileName );
1118 
1119  // now get any properties
1120  _getProperties( *doc, mProperties );
1121 
1122  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
1123 
1124  dump_( mProperties );
1125 
1126  // get older style project title
1127  QString oldTitle;
1128  _getTitle( *doc, oldTitle );
1129 
1130  QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1131  if ( homePathNl.count() > 0 )
1132  {
1133  QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1134  QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1135  if ( !homePath.isEmpty() )
1136  setPresetHomePath( homePath );
1137  }
1138  else
1139  {
1140  emit homePathChanged();
1141  }
1142 
1143  QgsReadWriteContext context;
1144  context.setPathResolver( pathResolver() );
1145  context.setProjectTranslator( this );
1146 
1147  //crs
1148  QgsCoordinateReferenceSystem projectCrs;
1149  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1150  {
1151  // first preference - dedicated projectCrs node
1152  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1153  if ( !srsNode.isNull() )
1154  {
1155  projectCrs.readXml( srsNode );
1156  }
1157 
1158  if ( !projectCrs.isValid() )
1159  {
1160  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1161  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1162 
1163  // try the CRS
1164  if ( currentCRS >= 0 )
1165  {
1166  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1167  }
1168 
1169  // if that didn't produce a match, try the proj.4 string
1170  if ( !projCrsString.isEmpty() && ( !projectCrs.isValid() || projectCrs.toProj4() != projCrsString ) )
1171  {
1172  projectCrs = QgsCoordinateReferenceSystem::fromProj4( projCrsString );
1173  }
1174 
1175  // last just take the given id
1176  if ( !projectCrs.isValid() )
1177  {
1178  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1179  }
1180  }
1181  }
1182  mCrs = projectCrs;
1183 
1184  QStringList datumErrors;
1185  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) )
1186  {
1187  emit missingDatumTransforms( datumErrors );
1188  }
1189  emit transformContextChanged();
1190 
1191  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "projectMetadata" ) );
1192  if ( !nl.isEmpty() )
1193  {
1194  QDomElement metadataElement = nl.at( 0 ).toElement();
1195  mMetadata.readMetadataXml( metadataElement );
1196  }
1197  else
1198  {
1199  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1200  mMetadata = QgsProjectMetadata();
1201  }
1202  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1203  {
1204  // upgrade older title storage to storing within project metadata.
1205  mMetadata.setTitle( oldTitle );
1206  }
1207  emit metadataChanged();
1208 
1209  nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
1210  if ( nl.count() )
1211  {
1212  QDomElement transactionElement = nl.at( 0 ).toElement();
1213  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1214  mAutoTransaction = true;
1215  }
1216 
1217  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
1218  if ( nl.count() )
1219  {
1220  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
1221  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1222  mEvaluateDefaultValues = true;
1223  }
1224 
1225  nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
1226  if ( nl.count() )
1227  {
1228  QDomElement trustElement = nl.at( 0 ).toElement();
1229  if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1230  mTrustLayerMetadata = true;
1231  }
1232 
1233  // read the layer tree from project file
1234 
1235  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1236 
1237  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1238  if ( !layerTreeElem.isNull() )
1239  {
1240  mRootGroup->readChildrenFromXml( layerTreeElem, context );
1241  }
1242  else
1243  {
1244  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1245  }
1246 
1247  mLayerTreeRegistryBridge->setEnabled( false );
1248 
1249  // get the map layers
1250  QList<QDomNode> brokenNodes;
1251  bool clean = _getMapLayers( *doc, brokenNodes );
1252 
1253  // review the integrity of the retrieved map layers
1254  if ( !clean )
1255  {
1256  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1257 
1258  if ( !brokenNodes.isEmpty() )
1259  {
1260  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1261  }
1262 
1263  // we let a custom handler decide what to do with missing layers
1264  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1265  mBadLayerHandler->handleBadLayers( brokenNodes );
1266  }
1267 
1268  // Resolve references to other layers
1269  // Needs to be done here once all dependent layers are loaded
1270  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1271  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
1272  {
1273  it.value()->resolveReferences( this );
1274  }
1275 
1276  mLayerTreeRegistryBridge->setEnabled( true );
1277 
1278  // load embedded groups and layers
1279  loadEmbeddedNodes( mRootGroup );
1280 
1281  // now that layers are loaded, we can resolve layer tree's references to the layers
1282  mRootGroup->resolveReferences( this );
1283 
1284 
1285  if ( !layerTreeElem.isNull() )
1286  {
1287  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1288  }
1289 
1290  // Load pre 3.0 configuration
1291  QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1292  if ( !layerTreeCanvasElem.isNull( ) )
1293  {
1294  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1295  }
1296 
1297  // Convert pre 3.4 to create layers flags
1298  if ( QgsProjectVersion( 3, 4, 0 ) > fileVersion )
1299  {
1300  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1301  for ( const QString &layerId : requiredLayerIds )
1302  {
1303  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1304  {
1305  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1306  }
1307  }
1308  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1309  for ( const QString &layerId : disabledLayerIds )
1310  {
1311  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1312  {
1313  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1314  }
1315  }
1316  }
1317 
1318  // make sure the are just valid layers
1320 
1321  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1322 
1323  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1325  mMapThemeCollection->readXml( *doc );
1326 
1327  mLabelingEngineSettings->readSettingsFromProject( this );
1329 
1330  mAnnotationManager->readXml( doc->documentElement(), context );
1331  mLayoutManager->readXml( doc->documentElement(), *doc );
1332 
1333  // reassign change dependencies now that all layers are loaded
1334  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1335  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1336  {
1337  it.value()->setDependencies( it.value()->dependencies() );
1338  }
1339 
1340  mSnappingConfig.readProject( *doc );
1341  //add variables defined in project file
1342  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1343  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1344 
1345  mCustomVariables.clear();
1346  if ( variableNames.length() == variableValues.length() )
1347  {
1348  for ( int i = 0; i < variableNames.length(); ++i )
1349  {
1350  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1351  }
1352  }
1353  else
1354  {
1355  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1356  }
1357  emit customVariablesChanged();
1358  emit crsChanged();
1359  emit ellipsoidChanged( ellipsoid() );
1360 
1361  // read the project: used by map canvas and legend
1362  emit readProject( *doc );
1363  emit readProjectWithContext( *doc, context );
1364  emit snappingConfigChanged( mSnappingConfig );
1366 
1367  // if all went well, we're allegedly in pristine state
1368  if ( clean )
1369  setDirty( false );
1370 
1374 
1375  if ( mTranslator )
1376  {
1377  //project possibly translated -> rename it with locale postfix
1378  QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
1379  setFileName( newFileName );
1380 
1381  if ( write() )
1382  {
1383  setTitle( localeFileName );
1384  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Success );
1385  }
1386  else
1387  {
1388  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Critical );
1389  }
1390  }
1391  return true;
1392 }
1393 
1394 
1395 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group )
1396 {
1397 
1398  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1399  {
1400  if ( QgsLayerTree::isGroup( child ) )
1401  {
1402  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1403  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1404  {
1405  // make sure to convert the path from relative to absolute
1406  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1407  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1408  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList() );
1409  if ( newGroup )
1410  {
1411  QList<QgsLayerTreeNode *> clonedChildren;
1412  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
1413  clonedChildren << newGroupChild->clone();
1414  delete newGroup;
1415 
1416  childGroup->insertChildNodes( 0, clonedChildren );
1417  }
1418  }
1419  else
1420  {
1421  loadEmbeddedNodes( childGroup );
1422  }
1423  }
1424  else if ( QgsLayerTree::isLayer( child ) )
1425  {
1426  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1427  {
1428  QList<QDomNode> brokenNodes;
1429  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes );
1430  }
1431  }
1432 
1433  }
1434 }
1435 
1436 QVariantMap QgsProject::customVariables() const
1437 {
1438  return mCustomVariables;
1439 }
1440 
1441 void QgsProject::setCustomVariables( const QVariantMap &variables )
1442 {
1443  if ( variables == mCustomVariables )
1444  return;
1445 
1446  //write variable to project
1447  QStringList variableNames;
1448  QStringList variableValues;
1449 
1450  QVariantMap::const_iterator it = variables.constBegin();
1451  for ( ; it != variables.constEnd(); ++it )
1452  {
1453  variableNames << it.key();
1454  variableValues << it.value().toString();
1455  }
1456 
1457  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1458  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1459 
1460  mCustomVariables = variables;
1461 
1462  emit customVariablesChanged();
1463 }
1464 
1466 {
1467  *mLabelingEngineSettings = settings;
1469 }
1470 
1472 {
1473  return *mLabelingEngineSettings;
1474 }
1475 
1477 {
1478  return mLayerStore.get();
1479 }
1480 
1482 {
1483  return mLayerStore.get();
1484 }
1485 
1486 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1487 {
1488  QList<QgsVectorLayer *> layers;
1489  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1490  Q_FOREACH ( const QString &layerId, layerIds )
1491  {
1492  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1493  layers << vlayer;
1494  }
1495  return layers;
1496 }
1497 
1498 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1499 {
1500  QStringList list;
1501  Q_FOREACH ( QgsVectorLayer *layer, layers )
1502  list << layer->id();
1503  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1505 }
1506 
1508 {
1509  QgsExpressionContext context;
1510 
1513 
1514  return context;
1515 }
1516 
1517 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1518 {
1519  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1520 
1521  bool tgChanged = false;
1522 
1523  Q_FOREACH ( QgsMapLayer *layer, layers )
1524  {
1525  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1526  if ( vlayer )
1527  {
1528  if ( autoTransaction() )
1529  {
1530  if ( QgsTransaction::supportsTransaction( vlayer ) )
1531  {
1532  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1533  QString key = vlayer->providerType();
1534 
1535  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1536 
1537  if ( !tg )
1538  {
1539  tg = new QgsTransactionGroup();
1540  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1541  tgChanged = true;
1542  }
1543  tg->addLayer( vlayer );
1544  }
1545  }
1547  }
1548 
1549  if ( tgChanged )
1550  emit transactionGroupsChanged();
1551 
1552  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1553 
1554  // check if we have to update connections for layers with dependencies
1555  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1556  {
1557  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1558  if ( deps.contains( layer->id() ) )
1559  {
1560  // reconnect to change signals
1561  it.value()->setDependencies( deps );
1562  }
1563  }
1564  }
1565 
1566  if ( mSnappingConfig.addLayers( layers ) )
1567  emit snappingConfigChanged( mSnappingConfig );
1568 }
1569 
1570 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1571 {
1572  if ( mSnappingConfig.removeLayers( layers ) )
1573  emit snappingConfigChanged( mSnappingConfig );
1574 }
1575 
1576 void QgsProject::cleanTransactionGroups( bool force )
1577 {
1578  bool changed = false;
1579  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1580  {
1581  if ( tg.value()->isEmpty() || force )
1582  {
1583  delete tg.value();
1584  tg = mTransactionGroups.erase( tg );
1585  changed = true;
1586  }
1587  else
1588  {
1589  ++tg;
1590  }
1591  }
1592  if ( changed )
1593  emit transactionGroupsChanged();
1594 }
1595 
1596 bool QgsProject::readLayer( const QDomNode &layerNode )
1597 {
1598  QgsReadWriteContext context;
1599  context.setPathResolver( pathResolver() );
1600  context.setProjectTranslator( this );
1601  QList<QDomNode> brokenNodes;
1602  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
1603  {
1604  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1605  // added layer for joins
1606  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1607  Q_FOREACH ( QgsVectorLayer *layer, vectorLayers )
1608  {
1609  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1610  layer->resolveReferences( this );
1611  }
1612 
1613  return true;
1614  }
1615  return false;
1616 }
1617 
1618 bool QgsProject::write( const QString &filename )
1619 {
1620  mFile.setFileName( filename );
1621 
1622  return write();
1623 }
1624 
1626 {
1627  if ( QgsProjectStorage *storage = projectStorage() )
1628  {
1629  // for projects stored in a custom storage, we cannot use relative paths since the storage most likely
1630  // will not be in a file system
1631  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
1632 
1633  QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
1634  QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
1635 
1636  if ( !zip( tmpZipFilename ) )
1637  return false; // zip() already calls setError() when returning false
1638 
1639  QFile tmpZipFile( tmpZipFilename );
1640  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
1641  {
1642  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
1643  return false;
1644  }
1645 
1646  QgsReadWriteContext context;
1647  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
1648  {
1649  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
1650  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1651  if ( !messages.isEmpty() )
1652  err += QStringLiteral( "\n\n" ) + messages.last().message();
1653  setError( err );
1654  return false;
1655  }
1656 
1657  tmpZipFile.close();
1658  QFile::remove( tmpZipFilename );
1659 
1660  return true;
1661  }
1662 
1663  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1664  {
1665  return zip( mFile.fileName() );
1666  }
1667  else
1668  {
1669  // write project file even if the auxiliary storage is not correctly
1670  // saved
1671  const bool asOk = saveAuxiliaryStorage();
1672  const bool writeOk = writeProjectFile( mFile.fileName() );
1673 
1674  // errors raised during writing project file are more important
1675  if ( !asOk && writeOk )
1676  {
1677  const QString err = mAuxiliaryStorage->errorString();
1678  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
1679  }
1680 
1681  return asOk && writeOk;
1682  }
1683 }
1684 
1685 bool QgsProject::writeProjectFile( const QString &filename )
1686 {
1687  QFile projectFile( filename );
1688  clearError();
1689 
1690  // if we have problems creating or otherwise writing to the project file,
1691  // let's find out up front before we go through all the hand-waving
1692  // necessary to create all the Dom objects
1693  QFileInfo myFileInfo( projectFile );
1694  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1695  {
1696  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1697  .arg( projectFile.fileName() ) );
1698  return false;
1699  }
1700 
1701  QgsReadWriteContext context;
1702  context.setPathResolver( pathResolver() );
1703 
1704  QDomImplementation DomImplementation;
1705  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1706 
1707  QDomDocumentType documentType =
1708  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1709  QStringLiteral( "SYSTEM" ) );
1710  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1711 
1712  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1713  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1714  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1715 
1716  doc->appendChild( qgisNode );
1717 
1718  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
1719  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
1720  qgisNode.appendChild( homePathNode );
1721 
1722  // title
1723  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1724  qgisNode.appendChild( titleNode );
1725 
1726  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1727  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? 1 : 0 );
1728  qgisNode.appendChild( transactionNode );
1729 
1730  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1731  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? 1 : 0 );
1732  qgisNode.appendChild( evaluateDefaultValuesNode );
1733 
1734  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
1735  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? 1 : 0 );
1736  qgisNode.appendChild( trustNode );
1737 
1738  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1739  titleNode.appendChild( titleText );
1740 
1741  // write project CRS
1742  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
1743  mCrs.writeXml( srsNode, *doc );
1744  qgisNode.appendChild( srsNode );
1745 
1746  // write layer tree - make sure it is without embedded subgroups
1747  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1749  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
1750 
1751  clonedRoot->writeXml( qgisNode, context );
1752  delete clonedRoot;
1753 
1754  mSnappingConfig.writeProject( *doc );
1755 
1756  // let map canvas and legend write their information
1757  emit writeProject( *doc );
1758 
1759  // within top level node save list of layers
1760  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
1761 
1762  // Iterate over layers in zOrder
1763  // Call writeXml() on each
1764  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1765 
1766  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
1767  while ( li != layers.end() )
1768  {
1769  QgsMapLayer *ml = li.value();
1770 
1771  if ( ml )
1772  {
1773  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1774  if ( emIt == mEmbeddedLayers.constEnd() )
1775  {
1776  // general layer metadata
1777  QDomElement maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1778 
1779  ml->writeLayerXml( maplayerElem, *doc, context );
1780 
1781  emit writeMapLayer( ml, maplayerElem, *doc );
1782 
1783  projectLayersNode.appendChild( maplayerElem );
1784  }
1785  else
1786  {
1787  // layer defined in an external project file
1788  // only save embedded layer if not managed by a legend group
1789  if ( emIt.value().second )
1790  {
1791  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1792  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
1793  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
1794  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
1795  projectLayersNode.appendChild( mapLayerElem );
1796  }
1797  }
1798  }
1799  li++;
1800  }
1801 
1802  qgisNode.appendChild( projectLayersNode );
1803 
1804  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
1805  Q_FOREACH ( QgsMapLayer *layer, mRootGroup->customLayerOrder() )
1806  {
1807  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
1808  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
1809  layerOrderNode.appendChild( mapLayerElem );
1810  }
1811  qgisNode.appendChild( layerOrderNode );
1812 
1813  mLabelingEngineSettings->writeSettingsToProject( this );
1814 
1815  // now add the optional extra properties
1816 
1817  dump_( mProperties );
1818 
1819  QgsDebugMsg( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
1820 
1821  if ( !mProperties.isEmpty() ) // only worry about properties if we
1822  // actually have any properties
1823  {
1824  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
1825  }
1826 
1827  mMapThemeCollection->writeXml( *doc );
1828 
1829  mTransformContext.writeXml( qgisNode, context );
1830 
1831  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
1832  mMetadata.writeMetadataXml( metadataElem, *doc );
1833  qgisNode.appendChild( metadataElem );
1834 
1835  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
1836  qgisNode.appendChild( annotationsElem );
1837 
1838  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
1839  qgisNode.appendChild( layoutElem );
1840 
1841  // now wrap it up and ship it to the project file
1842  doc->normalize(); // XXX I'm not entirely sure what this does
1843 
1844  // Create backup file
1845  if ( QFile::exists( fileName() ) )
1846  {
1847  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
1848  bool ok = true;
1849  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1850  ok &= projectFile.open( QIODevice::ReadOnly );
1851 
1852  QByteArray ba;
1853  while ( ok && !projectFile.atEnd() )
1854  {
1855  ba = projectFile.read( 10240 );
1856  ok &= backupFile.write( ba ) == ba.size();
1857  }
1858 
1859  projectFile.close();
1860  backupFile.close();
1861 
1862  if ( !ok )
1863  {
1864  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1865  return false;
1866  }
1867 
1868  QFileInfo fi( fileName() );
1869  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1870  utime( backupFile.fileName().toUtf8().constData(), &tb );
1871  }
1872 
1873  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1874  {
1875  projectFile.close(); // even though we got an error, let's make
1876  // sure it's closed anyway
1877 
1878  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
1879  return false;
1880  }
1881 
1882  QTemporaryFile tempFile;
1883  bool ok = tempFile.open();
1884  if ( ok )
1885  {
1886  QTextStream projectFileStream( &tempFile );
1887  doc->save( projectFileStream, 2 ); // save as utf-8
1888  ok &= projectFileStream.pos() > -1;
1889 
1890  ok &= tempFile.seek( 0 );
1891 
1892  QByteArray ba;
1893  while ( ok && !tempFile.atEnd() )
1894  {
1895  ba = tempFile.read( 10240 );
1896  ok &= projectFile.write( ba ) == ba.size();
1897  }
1898 
1899  ok &= projectFile.error() == QFile::NoError;
1900 
1901  projectFile.close();
1902  }
1903 
1904  tempFile.close();
1905 
1906  if ( !ok )
1907  {
1908  setError( tr( "Unable to save to file %1. Your project "
1909  "may be corrupted on disk. Try clearing some space on the volume and "
1910  "check file permissions before pressing save again." )
1911  .arg( projectFile.fileName() ) );
1912  return false;
1913  }
1914 
1915  setDirty( false ); // reset to pristine state
1916 
1917  emit projectSaved();
1918 
1919  return true;
1920 }
1921 
1922 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
1923 {
1924  bool propertiesModified;
1925  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
1926 
1927  if ( propertiesModified )
1928  setDirty( true );
1929 
1930  return success;
1931 }
1932 
1933 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
1934 {
1935  bool propertiesModified;
1936  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
1937 
1938  if ( propertiesModified )
1939  setDirty( true );
1940 
1941  return success;
1942 }
1943 
1944 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
1945 {
1946  bool propertiesModified;
1947  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
1948 
1949  if ( propertiesModified )
1950  setDirty( true );
1951 
1952  return success;
1953 }
1954 
1955 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
1956 {
1957  bool propertiesModified;
1958  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
1959 
1960  if ( propertiesModified )
1961  setDirty( true );
1962 
1963  return success;
1964 }
1965 
1966 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
1967 {
1968  bool propertiesModified;
1969  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
1970 
1971  if ( propertiesModified )
1972  setDirty( true );
1973 
1974  return success;
1975 }
1976 
1977 QStringList QgsProject::readListEntry( const QString &scope,
1978  const QString &key,
1979  const QStringList &def,
1980  bool *ok ) const
1981 {
1982  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1983 
1984  QVariant value;
1985 
1986  if ( property )
1987  {
1988  value = property->value();
1989 
1990  bool valid = QVariant::StringList == value.type();
1991  if ( ok )
1992  *ok = valid;
1993 
1994  if ( valid )
1995  {
1996  return value.toStringList();
1997  }
1998  }
1999 
2000  return def;
2001 }
2002 
2003 
2004 QString QgsProject::readEntry( const QString &scope,
2005  const QString &key,
2006  const QString &def,
2007  bool *ok ) const
2008 {
2009  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2010 
2011  QVariant value;
2012 
2013  if ( property )
2014  {
2015  value = property->value();
2016 
2017  bool valid = value.canConvert( QVariant::String );
2018  if ( ok )
2019  *ok = valid;
2020 
2021  if ( valid )
2022  return value.toString();
2023  }
2024 
2025  return def;
2026 }
2027 
2028 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2029  bool *ok ) const
2030 {
2031  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2032 
2033  QVariant value;
2034 
2035  if ( property )
2036  {
2037  value = property->value();
2038  }
2039 
2040  bool valid = value.canConvert( QVariant::Int );
2041 
2042  if ( ok )
2043  {
2044  *ok = valid;
2045  }
2046 
2047  if ( valid )
2048  {
2049  return value.toInt();
2050  }
2051 
2052  return def;
2053 }
2054 
2055 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2056  double def,
2057  bool *ok ) const
2058 {
2059  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2060  if ( property )
2061  {
2062  QVariant value = property->value();
2063 
2064  bool valid = value.canConvert( QVariant::Double );
2065  if ( ok )
2066  *ok = valid;
2067 
2068  if ( valid )
2069  return value.toDouble();
2070  }
2071 
2072  return def;
2073 }
2074 
2075 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
2076  bool *ok ) const
2077 {
2078  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2079 
2080  if ( property )
2081  {
2082  QVariant value = property->value();
2083 
2084  bool valid = value.canConvert( QVariant::Bool );
2085  if ( ok )
2086  *ok = valid;
2087 
2088  if ( valid )
2089  return value.toBool();
2090  }
2091 
2092  return def;
2093 }
2094 
2095 
2096 bool QgsProject::removeEntry( const QString &scope, const QString &key )
2097 {
2098  if ( findKey_( scope, key, mProperties ) )
2099  {
2100  removeKey_( scope, key, mProperties );
2101  setDirty( true );
2102  }
2103 
2104  return !findKey_( scope, key, mProperties );
2105 }
2106 
2107 
2108 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
2109 {
2110  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2111 
2112  QStringList entries;
2113 
2114  if ( foundProperty )
2115  {
2116  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2117 
2118  if ( propertyKey )
2119  { propertyKey->entryList( entries ); }
2120  }
2121 
2122  return entries;
2123 }
2124 
2125 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
2126 {
2127  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2128 
2129  QStringList entries;
2130 
2131  if ( foundProperty )
2132  {
2133  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2134 
2135  if ( propertyKey )
2136  { propertyKey->subkeyList( entries ); }
2137  }
2138 
2139  return entries;
2140 }
2141 
2143 {
2144  dump_( mProperties );
2145 }
2146 
2148 {
2149  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
2150  return QgsPathResolver( absolutePaths ? QString() : fileName() );
2151 }
2152 
2153 QString QgsProject::readPath( const QString &src ) const
2154 {
2155  return pathResolver().readPath( src );
2156 }
2157 
2158 QString QgsProject::writePath( const QString &src ) const
2159 {
2160  return pathResolver().writePath( src );
2161 }
2162 
2163 void QgsProject::setError( const QString &errorMessage )
2164 {
2165  mErrorMessage = errorMessage;
2166 }
2167 
2168 QString QgsProject::error() const
2169 {
2170  return mErrorMessage;
2171 }
2172 
2173 void QgsProject::clearError()
2174 {
2175  setError( QString() );
2176 }
2177 
2179 {
2180  delete mBadLayerHandler;
2181  mBadLayerHandler = handler;
2182 }
2183 
2184 QString QgsProject::layerIsEmbedded( const QString &id ) const
2185 {
2186  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
2187  if ( it == mEmbeddedLayers.constEnd() )
2188  {
2189  return QString();
2190  }
2191  return it.value().first;
2192 }
2193 
2194 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
2195  bool saveFlag )
2196 {
2197  QgsDebugCall;
2198 
2199  static QString sPrevProjectFilePath;
2200  static QDateTime sPrevProjectFileTimestamp;
2201  static QDomDocument sProjectDocument;
2202 
2203  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
2204 
2205  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
2206  {
2207  sPrevProjectFilePath.clear();
2208 
2209  QFile projectFile( projectFilePath );
2210  if ( !projectFile.open( QIODevice::ReadOnly ) )
2211  {
2212  return false;
2213  }
2214 
2215  if ( !sProjectDocument.setContent( &projectFile ) )
2216  {
2217  return false;
2218  }
2219 
2220  sPrevProjectFilePath = projectFilePath;
2221  sPrevProjectFileTimestamp = projectFileTimestamp;
2222  }
2223 
2224  // does project store paths absolute or relative?
2225  bool useAbsolutePaths = true;
2226 
2227  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
2228  if ( !propertiesElem.isNull() )
2229  {
2230  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
2231  if ( !absElem.isNull() )
2232  {
2233  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
2234  }
2235  }
2236 
2237  QgsReadWriteContext embeddedContext;
2238  if ( !useAbsolutePaths )
2239  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
2240  embeddedContext.setProjectTranslator( this );
2241 
2242  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
2243  if ( projectLayersElem.isNull() )
2244  {
2245  return false;
2246  }
2247 
2248  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
2249  for ( int i = 0; i < mapLayerNodes.size(); ++i )
2250  {
2251  // get layer id
2252  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
2253  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
2254  if ( id == layerId )
2255  {
2256  // layer can be embedded only once
2257  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
2258  {
2259  return false;
2260  }
2261 
2262  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
2263 
2264  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext ) )
2265  {
2266  return true;
2267  }
2268  else
2269  {
2270  mEmbeddedLayers.remove( layerId );
2271  return false;
2272  }
2273  }
2274  }
2275 
2276  return false;
2277 }
2278 
2279 
2280 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
2281 {
2282  // open project file, get layer ids in group, add the layers
2283  QFile projectFile( projectFilePath );
2284  if ( !projectFile.open( QIODevice::ReadOnly ) )
2285  {
2286  return nullptr;
2287  }
2288 
2289  QDomDocument projectDocument;
2290  if ( !projectDocument.setContent( &projectFile ) )
2291  {
2292  return nullptr;
2293  }
2294 
2295  QgsReadWriteContext context;
2296  context.setPathResolver( pathResolver() );
2297  context.setProjectTranslator( this );
2298 
2300 
2301  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2302  if ( !layerTreeElem.isNull() )
2303  {
2304  root->readChildrenFromXml( layerTreeElem, context );
2305  }
2306  else
2307  {
2308  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2309  }
2310 
2311  QgsLayerTreeGroup *group = root->findGroup( groupName );
2312  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
2313  {
2314  // embedded groups cannot be embedded again
2315  delete root;
2316  return nullptr;
2317  }
2318 
2319  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
2320  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
2321  delete root;
2322  root = nullptr;
2323 
2324  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2325  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2326 
2327  // set "embedded" to all children + load embedded layers
2328  mLayerTreeRegistryBridge->setEnabled( false );
2329  initializeEmbeddedSubtree( projectFilePath, newGroup );
2330  mLayerTreeRegistryBridge->setEnabled( true );
2331 
2332  // consider the layers might be identify disabled in its project
2333  Q_FOREACH ( const QString &layerId, newGroup->findLayerIds() )
2334  {
2335  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2336  if ( layer )
2337  {
2338  layer->resolveReferences( this );
2339  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
2340  }
2341  }
2342 
2343  return newGroup;
2344 }
2345 
2346 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group )
2347 {
2348  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
2349  {
2350  // all nodes in the subtree will have "embedded" custom property set
2351  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2352 
2353  if ( QgsLayerTree::isGroup( child ) )
2354  {
2355  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
2356  }
2357  else if ( QgsLayerTree::isLayer( child ) )
2358  {
2359  // load the layer into our project
2360  QList<QDomNode> brokenNodes;
2361  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false );
2362  }
2363  }
2364 }
2365 
2367 {
2368  return mEvaluateDefaultValues;
2369 }
2370 
2372 {
2373  if ( evaluateDefaultValues == mEvaluateDefaultValues )
2374  return;
2375 
2376  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2377  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
2378  for ( ; layerIt != layers.constEnd(); ++layerIt )
2379  {
2380  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
2381  if ( vl )
2382  {
2384  }
2385  }
2386 
2387  mEvaluateDefaultValues = evaluateDefaultValues;
2388 }
2389 
2391 {
2392  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2394 }
2395 
2397 {
2398  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2399 }
2400 
2402 {
2403  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2404  if ( !distanceUnitString.isEmpty() )
2405  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2406 
2407  //fallback to QGIS default measurement unit
2408  QgsSettings s;
2409  bool ok = false;
2410  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2411  return ok ? type : QgsUnitTypes::DistanceMeters;
2412 }
2413 
2415 {
2416  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2417 }
2418 
2420 {
2421  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2422  if ( !areaUnitString.isEmpty() )
2423  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2424 
2425  //fallback to QGIS default area unit
2426  QgsSettings s;
2427  bool ok = false;
2428  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2429  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2430 }
2431 
2433 {
2434  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2435 }
2436 
2437 QString QgsProject::homePath() const
2438 {
2439  if ( !mHomePath.isEmpty() )
2440  {
2441  QFileInfo homeInfo( mHomePath );
2442  if ( !homeInfo.isRelative() )
2443  return mHomePath;
2444  }
2445 
2446  QFileInfo pfi( fileName() );
2447  if ( !pfi.exists() )
2448  return mHomePath;
2449 
2450  if ( !mHomePath.isEmpty() )
2451  {
2452  // path is relative to project file
2453  return QDir::cleanPath( pfi.path() + '/' + mHomePath );
2454  }
2455  else
2456  {
2457  return pfi.canonicalPath();
2458  }
2459 }
2460 
2462 {
2463  return mHomePath;
2464 }
2465 
2467 {
2468  return mRelationManager;
2469 }
2470 
2472 {
2473  return mLayoutManager.get();
2474 }
2475 
2477 {
2478  return mLayoutManager.get();
2479 }
2480 
2482 {
2483  return mRootGroup;
2484 }
2485 
2487 {
2488  return mMapThemeCollection.get();
2489 }
2490 
2492 {
2493  return mAnnotationManager.get();
2494 }
2495 
2497 {
2498  return mAnnotationManager.get();
2499 }
2500 
2501 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2502 {
2503  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
2504  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
2505  {
2506  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2507  continue;
2508 
2509  if ( layers.contains( it.value() ) )
2510  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
2511  else
2512  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
2513  }
2514 
2518 }
2519 
2520 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2521 {
2522  QList<QgsMapLayer *> nonIdentifiableLayers;
2523  nonIdentifiableLayers.reserve( layerIds.count() );
2524  for ( const QString &layerId : layerIds )
2525  {
2526  QgsMapLayer *layer = mapLayer( layerId );
2527  if ( layer )
2528  nonIdentifiableLayers << layer;
2529  }
2531  setNonIdentifiableLayers( nonIdentifiableLayers );
2533 }
2534 
2535 QStringList QgsProject::nonIdentifiableLayers() const
2536 {
2537  QStringList nonIdentifiableLayers;
2538 
2539  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2540  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
2541  {
2542  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2543  {
2544  nonIdentifiableLayers.append( it.value()->id() );
2545  }
2546  }
2547  return nonIdentifiableLayers;
2548 }
2549 
2551 {
2552  return mAutoTransaction;
2553 }
2554 
2556 {
2557  if ( autoTransaction != mAutoTransaction )
2558  {
2559  mAutoTransaction = autoTransaction;
2560 
2561  if ( autoTransaction )
2562  onMapLayersAdded( mapLayers().values() );
2563  else
2564  cleanTransactionGroups( true );
2565  }
2566 }
2567 
2568 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2569 {
2570  return mTransactionGroups;
2571 }
2572 
2573 
2574 //
2575 // QgsMapLayerStore methods
2576 //
2577 
2578 
2580 {
2581  return mLayerStore->count();
2582 }
2583 
2584 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
2585 {
2586  return mLayerStore->mapLayer( layerId );
2587 }
2588 
2589 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
2590 {
2591  return mLayerStore->mapLayersByName( layerName );
2592 }
2593 
2594 bool QgsProject::unzip( const QString &filename )
2595 {
2596  clearError();
2597  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2598 
2599  // unzip the archive
2600  if ( !archive->unzip( filename ) )
2601  {
2602  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
2603  return false;
2604  }
2605 
2606  // test if zip provides a .qgs file
2607  if ( archive->projectFile().isEmpty() )
2608  {
2609  setError( tr( "Zip archive does not provide a project file" ) );
2610  return false;
2611  }
2612 
2613  // load auxiliary storage
2614  if ( !archive->auxiliaryStorageFile().isEmpty() )
2615  {
2616  // database file is already a copy as it's been unzipped. So we don't open
2617  // auxiliary storage in copy mode in this case
2618  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
2619  }
2620  else
2621  {
2622  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
2623  }
2624 
2625  // read the project file
2626  if ( ! readProjectFile( archive->projectFile() ) )
2627  {
2628  setError( tr( "Cannot read unzipped qgs project file" ) );
2629  return false;
2630  }
2631 
2632  // keep the archive and remove the temporary .qgs file
2633  mArchive = std::move( archive );
2634  mArchive->clearProjectFile();
2635 
2636  return true;
2637 }
2638 
2639 bool QgsProject::zip( const QString &filename )
2640 {
2641  clearError();
2642 
2643  // save the current project in a temporary .qgs file
2644  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2645  const QString baseName = QFileInfo( filename ).baseName();
2646  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
2647  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
2648 
2649  bool writeOk = false;
2650  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2651  {
2652  writeOk = writeProjectFile( qgsFile.fileName() );
2653  qgsFile.close();
2654  }
2655 
2656  // stop here with an error message
2657  if ( ! writeOk )
2658  {
2659  setError( tr( "Unable to write temporary qgs file" ) );
2660  return false;
2661  }
2662 
2663  // save auxiliary storage
2664  const QFileInfo info( qgsFile );
2665  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
2666 
2667  if ( ! saveAuxiliaryStorage( asFileName ) )
2668  {
2669  const QString err = mAuxiliaryStorage->errorString();
2670  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2671  return false;
2672  }
2673 
2674  // create the archive
2675  archive->addFile( qgsFile.fileName() );
2676  archive->addFile( asFileName );
2677 
2678  // zip
2679  if ( !archive->zip( filename ) )
2680  {
2681  setError( tr( "Unable to perform zip" ) );
2682  return false;
2683  }
2684 
2685  return true;
2686 }
2687 
2689 {
2690  return QgsZipUtils::isZipFile( mFile.fileName() );
2691 }
2692 
2693 QList<QgsMapLayer *> QgsProject::addMapLayers(
2694  const QList<QgsMapLayer *> &layers,
2695  bool addToLegend,
2696  bool takeOwnership )
2697 {
2698  const QList<QgsMapLayer *> myResultList = mLayerStore->addMapLayers( layers, takeOwnership );
2699  if ( !myResultList.isEmpty() )
2700  {
2701  if ( addToLegend )
2702  emit legendLayersAdded( myResultList );
2703  }
2704 
2705  if ( mAuxiliaryStorage )
2706  {
2707  for ( QgsMapLayer *mlayer : myResultList )
2708  {
2709  if ( mlayer->type() != QgsMapLayer::VectorLayer )
2710  continue;
2711 
2712  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
2713  if ( vl )
2714  {
2715  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
2716  }
2717  }
2718  }
2719 
2720  return myResultList;
2721 }
2722 
2723 QgsMapLayer *
2725  bool addToLegend,
2726  bool takeOwnership )
2727 {
2728  QList<QgsMapLayer *> addedLayers;
2729  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
2730  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
2731 }
2732 
2733 void QgsProject::removeMapLayers( const QStringList &layerIds )
2734 {
2735  mLayerStore->removeMapLayers( layerIds );
2736 }
2737 
2738 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
2739 {
2740  mLayerStore->removeMapLayers( layers );
2741 }
2742 
2743 void QgsProject::removeMapLayer( const QString &layerId )
2744 {
2745  mLayerStore->removeMapLayer( layerId );
2746 }
2747 
2749 {
2750  mLayerStore->removeMapLayer( layer );
2751 }
2752 
2754 {
2755  return mLayerStore->takeMapLayer( layer );
2756 }
2757 
2759 {
2760  mLayerStore->removeAllMapLayers();
2761 }
2762 
2764 {
2765  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2766  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
2767  for ( ; it != layers.constEnd(); ++it )
2768  {
2769  it.value()->reload();
2770  }
2771 }
2772 
2773 QMap<QString, QgsMapLayer *> QgsProject::mapLayers() const
2774 {
2775  return mLayerStore->mapLayers();
2776 }
2777 
2778 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
2779 {
2780  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
2781 }
2782 
2784 {
2785  QgsSettings settings;
2786  QgsCoordinateReferenceSystem defaultCrs;
2787  if ( settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "useProject" )
2788  || settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "prompt" ) )
2789  {
2790  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
2791  // (since "prompt" has no meaning here - the prompt will always be shown, it's just deciding on the default choice in the prompt!)
2792  defaultCrs = crs();
2793  }
2794  else
2795  {
2796  // global crs
2797  QString layerDefaultCrs = settings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), GEO_EPSG_CRS_AUTHID ).toString();
2798  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
2799  }
2800 
2801  return defaultCrs;
2802 }
2803 
2805 {
2806  mTrustLayerMetadata = trust;
2807 
2808  auto layers = mapLayers();
2809  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
2810  {
2811  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
2812  if ( vl )
2813  {
2814  vl->setReadExtentFromXml( trust );
2815  }
2816  }
2817 }
2818 
2819 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
2820 {
2821  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2822  bool empty = true;
2823  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
2824  {
2825  if ( it.value()->type() != QgsMapLayer::VectorLayer )
2826  continue;
2827 
2828  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
2829  if ( vl && vl->auxiliaryLayer() )
2830  {
2831  vl->auxiliaryLayer()->save();
2832  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
2833  }
2834  }
2835 
2836  if ( !mAuxiliaryStorage->exists( *this ) && filename.isEmpty() && empty )
2837  {
2838  return true; // it's not an error
2839  }
2840  else if ( !filename.isEmpty() )
2841  {
2842  return mAuxiliaryStorage->saveAs( filename );
2843  }
2844  else
2845  {
2846  return mAuxiliaryStorage->saveAs( *this );
2847  }
2848 }
2849 
2851 {
2852  return mAuxiliaryStorage.get();
2853 }
2854 
2856 {
2857  return mAuxiliaryStorage.get();
2858 }
2859 
2861 {
2862  return mMetadata;
2863 }
2864 
2866 {
2867  if ( metadata == mMetadata )
2868  return;
2869 
2870  mMetadata = metadata;
2871  emit metadataChanged();
2872 
2873  setDirty( true );
2874 }
2875 
2876 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
2877 {
2878  QSet<QgsMapLayer *> requiredLayers;
2879 
2880  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2881  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
2882  {
2883  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
2884  {
2885  requiredLayers.insert( it.value() );
2886  }
2887  }
2888  return requiredLayers;
2889 }
2890 
2891 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
2892 {
2893  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
2894  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
2895  {
2896  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
2897  continue;
2898 
2899  if ( layers.contains( it.value() ) )
2900  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
2901  else
2902  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
2903  }
2904 }
2905 
2906 void QgsProject::generateTsFile( const QString &locale )
2907 {
2908  QgsTranslationContext translationContext;
2909  translationContext.setProject( this );
2910  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
2911 
2912  emit QgsApplication::instance()->collectTranslatableObjects( &translationContext );
2913 
2914  translationContext.writeTsFile( locale );
2915 }
2916 
2917 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
2918 {
2919  if ( !mTranslator )
2920  {
2921  return sourceText;
2922  }
2923 
2924  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
2925 
2926  if ( result.isEmpty() )
2927  {
2928  return sourceText;
2929  }
2930  return result;
2931 }
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
int count() const
Returns the number of sub-keys contained by this property.
Layer tree group node serves as a container for layers and further groups.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:441
void setTrustLayerMetadata(bool trust)
Sets the trust option allowing to indicate if the extent has to be read from the XML document when da...
bool topologicalEditing() const
Convenience function to query topological editing status.
The class is used as a container of context for various read/write operations on other objects...
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QString name
Name of the project - equivalent to a file&#39;s base name (i.e. without path and extension).
static Q_INVOKABLE AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
Base class for all map layer types.
Definition: qgsmaplayer.h:63
static void removeInvalidLayers(QgsLayerTreeGroup *group)
Remove layer nodes that refer to invalid layers.
QList< QgsLayerTreeGroup * > findGroups() const
Find all group layer nodes.
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:431
static QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj4 style formatted string.
static const QString QGIS_VERSION
Version string.
Definition: qgis.h:64
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsMapLayer::LayerType type() const
Returns the type of the layer.
Q_DECL_DEPRECATED QStringList nonIdentifiableLayers() const
Gets the list of layers which currently should not be taken into account on map identification.
bool isValid() const
Returns the status of the layer.
This is an abstract base class for any elements of a drag and drop form.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
void isDirtyChanged(bool dirty)
Emitted when the project dirty status changes.
QgsTransactionGroup * transactionGroup(const QString &providerKey, const QString &connString)
Returns the matching transaction group from a provider key and connection string. ...
QString absolutePath() const
Returns full absolute path to the project folder if the project is stored in a file system - derived ...
Definition: qgsproject.cpp:581
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the store.
bool save()
Commits changes and starts editing then.
Manages storage of a set of QgsAnnotation annotation objects.
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
Definition: qgsproject.cpp:721
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ...
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
Definition: qgsproject.cpp:843
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file...
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
Q_DECL_DEPRECATED void setNonIdentifiableLayers(const QList< QgsMapLayer * > &layers)
Set a list of layers which should not be taken into account on map identification.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
QgsSnappingConfig snappingConfig() const
The snapping configuration for this project.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
const QgsProjectMetadata & metadata() const
Returns a reference to the project&#39;s metadata store.
Class used to work with layer dependencies stored in a XML project or layer definition file...
int count() const
Returns the number of registered layers.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
void setCreationDateTime(const QDateTime &creationDateTime)
Sets the project&#39;s creation date/timestamp.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void legendLayersAdded(const QList< QgsMapLayer * > &layers)
Emitted, when a layer was added to the registry and the legend.
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:614
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:535
Class providing some utility methods to manage auxiliary storage.
void crsChanged()
Emitted when the CRS of the project has changed.
void generateTsFile(const QString &locale)
Triggers the collection strings of .qgs to be included in ts file and calls writeTsFile() ...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the store.
QString presetHomePath() const
Returns any manual project home path setting, or an empty string if not set.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void configChanged()
Emitted whenever the configuration is changed.
Container of fields for a vector layer.
Definition: qgsfields.h:42
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
void labelingEngineSettingsChanged()
Emitted when global configuration of the labeling engine changes.
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context)
Sets state from DOM document.
QString source() const
Returns the source for the layer.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application&#39;s plugin layer registry, used for managing plugin layer types.
void topologicalEditingChanged()
Emitted when the topological editing flag has changed.
void metadataChanged()
Emitted when the project&#39;s metadata is changed.
QString layerIsEmbedded(const QString &id) const
Returns project file path if layer is embedded from other project file. Returns empty string if layer...
QDateTime lastModified
Date and local time when the file was last modified.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
QString homePath() const
Returns the project&#39;s home path.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:567
void reloadAllLayers()
Reload all registered layer&#39;s provider data caches, synchronising the layer with any changes in the d...
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
void resolveReferences(QgsProject *project) FINAL
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects...
void removeKey(const QString &keyName)
Removes the specified key.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
void projectSaved()
Emitted when the project file has been written and closed.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the store.
void reset()
reset to default values
void allLayersRemoved()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results...
QgsMapLayerStore * layerStore()
Returns a pointer to the project&#39;s internal layer store.
void clear()
Remove any relation managed by this class.
QgsProject(QObject *parent=nullptr)
Create a new QgsProject.
Definition: qgsproject.cpp:349
static QString extension()
Returns the extension used for auxiliary databases.
static QString userFullName()
Returns the user&#39;s operating system login account full display name.
QgsProjectVersion getVersion(const QDomDocument &doc)
Returns the version string found in the given DOM document.
Definition: qgsproject.cpp:820
const QString GEO_NONE
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.cpp:71
QString name() const
Returns the name of this element.
void fileNameChanged()
Emitted when the file name of the project changes.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:115
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the registry.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context&#39;s state from a DOM element.
bool readMetadataXml(const QDomElement &metadataElement) override
Sets state from DOM document.
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
void setAuthor(const QString &author)
Sets the project author string.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
const QgsLayoutManager * layoutManager() const
Returns the project&#39;s layout manager, which manages compositions within the project.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean entry to the project file.
QgsProjectPropertyValue * setValue(const QString &name, const QVariant &value)
Sets the value associated with this key.
void readSettings()
Reads the context&#39;s state from application settings.
void missingDatumTransforms(const QStringList &missingTransforms)
Emitted when datum transforms stored in the project are not available locally.
bool read()
Reads the project from its currently associated file (see fileName() ).
Definition: qgsproject.cpp:985
void readProjectWithContext(const QDomDocument &, QgsReadWriteContext &context)
Emitted when a project is being read.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QStringList subkeyList(const QString &scope, const QString &key) const
Returns keys with keys – do not return keys that contain only values.
QgsProjectStorage * projectStorage() const
Returns pointer to project storage implementation that handles read/write of the project file...
Definition: qgsproject.cpp:562
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
QVariant value() const override
If this key has a value, it will be stored by its name in its properties.
QString name() const
The name of the property is used as identifier.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const
Returns the default CRS for new layers based on the settings and the current project CRS...
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
bool writeMetadataXml(QDomElement &metadataElement, QDomDocument &document) const override
Stores state in a DOM node.
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the default area measurement units for the project.
const QString & typeName
A class to describe the version of a project.
virtual void clearKeys()
Deletes any sub-nodes from the property.
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the store.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
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).
void readProject(const QDomDocument &)
Emitted when a project is being read.
QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const override
Translates the project with QTranslator and qm file.
Listens to the updates in map layer registry and does changes in layer tree.
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
void clear()
Clear any information from this layer tree.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the project&#39;s native coordinate reference system.
Definition: qgsproject.cpp:622
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
void layerLoaded(int i, int n)
Emitted when a layer from a projects was read.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context&#39;s state to a DOM element.
void dump(int tabs=0) const override
Dumps out the keys and values.
QString fileName() const
Returns the project&#39;s file name.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the default distance measurement units for the project.
Metadata associated with a project.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
This class is a base class for nodes in a layer tree.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
Reads and writes project states.
Definition: qgsproject.h:89
const QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:69
void setEllipsoid(const QString &ellipsoid)
Sets the project&#39;s ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:641
~QgsProject() override
Definition: qgsproject.cpp:392
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
QList< QgsReadWriteContext::ReadWriteMessage > takeMessages()
Returns the stored messages and remove them.
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted when a layer is being saved.
QString error() const
Returns error message from previous read/write.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
void removeAllMapLayers()
Removes all registered layers.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file...
virtual bool isValue() const =0
Returns true if the property is a QgsProjectPropertyValue.
QgsCoordinateTransformContext transformContext() const
Returns a copy of the project&#39;s coordinate transform context, which stores various information regard...
Contains information about the context in which a coordinate transform is executed.
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Remove a given key.
Definition: qgsproject.cpp:289
QList< QgsVectorLayer * > avoidIntersectionsLayers() const
A list of layers with which intersections should be avoided.
QgsEditFormConfig editFormConfig
bool addLayers(const QList< QgsMapLayer * > &layers)
Adds the specified layers as individual layers to the configuration with standard configuration...
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
Manages storage of a set of layouts.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the store.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
void setProviderProperty(ProviderProperty property, const QVariant &value)
Allows setting arbitrary properties on the provider.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
bool write()
Writes the project to its current associated file (see fileName() ).
Used for the collecting of strings from projects for translation and creation of ts files...
QgsAnnotationManager * annotationManager()
Returns pointer to the project&#39;s annotation manager.
#define QgsDebugCall
Definition: qgslogger.h:37
const QgsAuxiliaryStorage * auxiliaryStorage() const
Returns the current const auxiliary storage.
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
QStringList makeKeyTokens_(const QString &scope, const QString &key)
Take the given scope and key and convert them to a string list of key tokens that will be used to nav...
Definition: qgsproject.cpp:83
bool loadAuxiliaryLayer(const QgsAuxiliaryStorage &storage, const QString &key=QString())
Loads the auxiliary layer for this vector layer.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers)
Create layer group instance defined in an arbitrary project file.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
Definition: qgsproject.cpp:482
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
void setMetadata(const QgsProjectMetadata &metadata)
Sets the project&#39;s metadata store.
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
bool readLayer(const QDomNode &layerNode)
Reads the layer described in the associated DOM node.
QgsMapThemeCollection * mapThemeCollection()
Returns pointer to the project&#39;s map theme collection.
Project property key node.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:53
QgsRelationManager * relationManager() const
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:436
bool removeLayers(const QList< QgsMapLayer * > &layers)
Removes the specified layers from the individual layer configuration.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed...
QgsProjectProperty * find(const QString &propertyName) const
Attempts to find a property with a matching sub-key name.
void setPresetHomePath(const QString &path)
Sets the project&#39;s home path.
Definition: qgsproject.cpp:453
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
void writeTsFile(const QString &locale)
Writes the Ts-file.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:615
void setFileName(const QString &fileName)
Sets the fileName of the TS file.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString providerType() const
Returns the provider type for this layer.
QString baseName() const
Returns the base name of the project file without the path and without extension - derived from fileN...
Definition: qgsproject.cpp:603
void registerTranslation(const QString &context, const QString &source)
Registers the source to be translated.
QgsUnitTypes::DistanceUnit distanceUnits() const
Convenience function to query default distance measurement units for project.
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer * > &layers)
A list of layers with which intersections should be avoided.
void writeProject(QDomDocument &)
Emitted when the project is being written.
void oldProjectVersionWarning(const QString &)
Emitted when an old project file is read.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
Q_DECL_DEPRECATED QSet< QgsMapLayer * > requiredLayers() const
Returns a set of map layers that are required in the project and therefore they should not get remove...
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the registry.
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already) ...
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
Q_DECL_DEPRECATED void setRequiredLayers(const QSet< QgsMapLayer * > &layers)
Configures a set of map layers that are required in the project and therefore they should not get rem...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:738
void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project&#39;s global labeling engine settings.
void setProject(QgsProject *project)
Sets the project being translated.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
void registerTranslatableContainers(QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId)
Registers the containers that require translation into the translationContext.
Definition: qgsproject.cpp:464
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed...
This class manages a set of relations between layers.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QVariantMap customVariables() const
A map of custom project variables.
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, bool saveFlag=true)
Creates a maplayer instance defined in an arbitrary project file.
Stores global configuration for labeling engine.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:411
void removeMapLayer(const QString &layerId)
Remove a layer from the registry by layer ID.
CORE_EXPORT bool isZipFile(const QString &filename)
Returns true if the file name is a zipped file ( i.e with a &#39;.qgz&#39; extension, false otherwise...
Definition: qgsziputils.cpp:29
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
This class represents a coordinate reference system (CRS).
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project&#39;s coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:652
void setTitle(const QString &title)
Sets the project&#39;s title.
Definition: qgsproject.cpp:420
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
void loadingLayer(const QString &layerName)
Emitted when a layer is loaded.
Abstract interface for project storage - to be implemented by various backends and registered in QgsP...
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
Definition: qgsmaplayer.h:131
QStringList entryList(const QString &scope, const QString &key) const
Returns keys with values – do not return keys that contain other keys.
void loadingLayerMessageReceived(const QString &layerName, const QList< QgsReadWriteContext::ReadWriteMessage > &messages)
Emitted when loading layers has produced some messages.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
An Abstract Base Class for QGIS project property hierarchys.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
QString absoluteFilePath() const
Returns full absolute path to the project file if the project is stored in a file system - derived fr...
Definition: qgsproject.cpp:592
void homePathChanged()
Emitted when the home path of the project changes.
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:89
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
QString name() const override
Returns the group&#39;s name.
void dumpProperties() const
Dump out current project properties to stderr.
void avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
Container class that allows storage of map themes consisting of visible map layers and layer styles...
void transformContextChanged()
Emitted when the project transformContext() is changed.
This is a container for configuration of the snapping of the project.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project&#39;s layer tree.
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted after the basic initialization of a layer from the project file is done.
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project&#39;s associated file.
Definition: qgsproject.cpp:557
Resolves relative paths into absolute paths and vice versa.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:125
void setEvaluateDefaultValues(bool evaluateDefaultValues)
Defines if default values should be evaluated on provider side when requested and not when committed...
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
Represents a vector layer which manages a vector based data sets.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
QgsCoordinateReferenceSystem crs() const
Returns the project&#39;s native coordinate reference system.
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
QgsProjectStorage * projectStorageFromUri(const QString &uri)
Returns storage implementation if the URI matches one. Returns null pointer otherwise (it is a normal...
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
void setName(const QString &name)
The name of the property is used as identifier.
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value, bool &propertiesModified)
Add the given key and value.
Definition: qgsproject.cpp:202
bool isEmpty() const
Returns true if this property contains no sub-keys.
QString ellipsoid() const
Returns a proj string representing the project&#39;s ellipsoid setting, e.g., "WGS84".
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
AreaUnit
Units of area.
Definition: qgsunittypes.h:79
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:130
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
QgsPluginLayer * createLayer(const QString &typeName, const QString &uri=QString())
Returns new layer if corresponding plugin has been found else returns a nullptr.
void removeAll()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void _getProperties(const QDomDocument &doc, QgsProjectPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:758
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets project&#39;s global labeling engine settings.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
void clear()
Clears the project, removing all settings and resetting it back to an empty, default state...
Definition: qgsproject.cpp:661
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *project)
Updates an embedded group from a project.
Interface for classes that handle missing layer files when reading project file.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the store.
Q_DECL_DEPRECATED void nonIdentifiableLayersChanged(QStringList nonIdentifiableLayers)
Emitted when the list of layer which are excluded from map identification changes.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the registry.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file...
Layer tree node points to a map layer.
QString toProj4() const
Returns a Proj4 string representation of this CRS.
void setReadExtentFromXml(bool readExtentFromXml)
Flag allowing to indicate if the extent has to be read from the XML document when data source has no ...
A structured metadata store for a map layer.