QGIS API Documentation  3.12.1-București (121cc00ff0)
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 "qgsbookmarkmanager.h"
53 #include "qgsmaplayerstore.h"
54 #include "qgsziputils.h"
55 #include "qgsauxiliarystorage.h"
56 #include "qgssymbollayerutils.h"
57 #include "qgsapplication.h"
59 #include "qgsstyleentityvisitor.h"
60 #include "qgsprojectviewsettings.h"
62 
63 #include <algorithm>
64 #include <QApplication>
65 #include <QFileInfo>
66 #include <QDomNode>
67 #include <QObject>
68 #include <QTextStream>
69 #include <QTemporaryFile>
70 #include <QDir>
71 #include <QUrl>
72 
73 
74 #ifdef _MSC_VER
75 #include <sys/utime.h>
76 #else
77 #include <utime.h>
78 #endif
79 
80 // canonical project instance
81 QgsProject *QgsProject::sProject = nullptr;
82 
91 QStringList makeKeyTokens_( const QString &scope, const QString &key )
92 {
93  QStringList keyTokens = QStringList( scope );
94  keyTokens += key.split( '/', QString::SkipEmptyParts );
95 
96  // be sure to include the canonical root node
97  keyTokens.push_front( QStringLiteral( "properties" ) );
98 
99  //check validy of keys since an invalid xml name will will be dropped upon saving the xml file. If not valid, we print a message to the console.
100  for ( int i = 0; i < keyTokens.size(); ++i )
101  {
102  QString keyToken = keyTokens.at( i );
103 
104  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
105  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
106  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]" );
107  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]" );
108 
109  if ( keyToken.contains( QRegExp( nameCharRegexp ) ) || keyToken.contains( QRegExp( nameStartCharRegexp ) ) )
110  {
111 
112  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
113  QgsMessageLog::logMessage( errorString, QString(), Qgis::Critical );
114 
115  }
116 
117  }
118 
119  return keyTokens;
120 }
121 
122 
123 
133 QgsProjectProperty *findKey_( const QString &scope,
134  const QString &key,
135  QgsProjectPropertyKey &rootProperty )
136 {
137  QgsProjectPropertyKey *currentProperty = &rootProperty;
138  QgsProjectProperty *nextProperty; // link to next property down hierarchy
139 
140  QStringList keySequence = makeKeyTokens_( scope, key );
141 
142  while ( !keySequence.isEmpty() )
143  {
144  // if the current head of the sequence list matches the property name,
145  // then traverse down the property hierarchy
146  if ( keySequence.first() == currentProperty->name() )
147  {
148  // remove front key since we're traversing down a level
149  keySequence.pop_front();
150 
151  if ( 1 == keySequence.count() )
152  {
153  // if we have only one key name left, then return the key found
154  return currentProperty->find( keySequence.front() );
155  }
156  else if ( keySequence.isEmpty() )
157  {
158  // if we're out of keys then the current property is the one we
159  // want; i.e., we're in the rate case of being at the top-most
160  // property node
161  return currentProperty;
162  }
163  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
164  {
165  if ( nextProperty->isKey() )
166  {
167  currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
168  }
169  else if ( nextProperty->isValue() && 1 == keySequence.count() )
170  {
171  // it may be that this may be one of several property value
172  // nodes keyed by QDict string; if this is the last remaining
173  // key token and the next property is a value node, then
174  // that's the situation, so return the currentProperty
175  return currentProperty;
176  }
177  else
178  {
179  // QgsProjectPropertyValue not Key, so return null
180  return nullptr;
181  }
182  }
183  else
184  {
185  // if the next key down isn't found
186  // then the overall key sequence doesn't exist
187  return nullptr;
188  }
189  }
190  else
191  {
192  return nullptr;
193  }
194  }
195 
196  return nullptr;
197 }
198 
199 
200 
210 QgsProjectProperty *addKey_( const QString &scope,
211  const QString &key,
212  QgsProjectPropertyKey *rootProperty,
213  const QVariant &value,
214  bool &propertiesModified )
215 {
216  QStringList keySequence = makeKeyTokens_( scope, key );
217 
218  // cursor through property key/value hierarchy
219  QgsProjectPropertyKey *currentProperty = rootProperty;
220  QgsProjectProperty *nextProperty; // link to next property down hierarchy
221  QgsProjectPropertyKey *newPropertyKey = nullptr;
222 
223  propertiesModified = false;
224  while ( ! keySequence.isEmpty() )
225  {
226  // if the current head of the sequence list matches the property name,
227  // then traverse down the property hierarchy
228  if ( keySequence.first() == currentProperty->name() )
229  {
230  // remove front key since we're traversing down a level
231  keySequence.pop_front();
232 
233  // if key sequence has one last element, then we use that as the
234  // name to store the value
235  if ( 1 == keySequence.count() )
236  {
237  QgsProjectProperty *property = currentProperty->find( keySequence.front() );
238  if ( !property || property->value() != value )
239  {
240  currentProperty->setValue( keySequence.front(), value );
241  propertiesModified = true;
242  }
243 
244  return currentProperty;
245  }
246  // we're at the top element if popping the keySequence element
247  // will leave it empty; in that case, just add the key
248  else if ( keySequence.isEmpty() )
249  {
250  if ( currentProperty->value() != value )
251  {
252  currentProperty->setValue( value );
253  propertiesModified = true;
254  }
255 
256  return currentProperty;
257  }
258  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
259  {
260  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
261 
262  if ( currentProperty )
263  {
264  continue;
265  }
266  else // QgsProjectPropertyValue not Key, so return null
267  {
268  return nullptr;
269  }
270  }
271  else // the next subkey doesn't exist, so add it
272  {
273  if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
274  {
275  currentProperty = newPropertyKey;
276  }
277  continue;
278  }
279  }
280  else
281  {
282  return nullptr;
283  }
284  }
285 
286  return nullptr;
287 }
288 
297 void removeKey_( const QString &scope,
298  const QString &key,
299  QgsProjectPropertyKey &rootProperty )
300 {
301  QgsProjectPropertyKey *currentProperty = &rootProperty;
302 
303  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
304  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
305 
306  QStringList keySequence = makeKeyTokens_( scope, key );
307 
308  while ( ! keySequence.isEmpty() )
309  {
310  // if the current head of the sequence list matches the property name,
311  // then traverse down the property hierarchy
312  if ( keySequence.first() == currentProperty->name() )
313  {
314  // remove front key since we're traversing down a level
315  keySequence.pop_front();
316 
317  // if we have only one key name left, then try to remove the key
318  // with that name
319  if ( 1 == keySequence.count() )
320  {
321  currentProperty->removeKey( keySequence.front() );
322  }
323  // if we're out of keys then the current property is the one we
324  // want to remove, but we can't delete it directly; we need to
325  // delete it from the parent property key container
326  else if ( keySequence.isEmpty() )
327  {
328  previousQgsPropertyKey->removeKey( currentProperty->name() );
329  }
330  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
331  {
332  previousQgsPropertyKey = currentProperty;
333  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
334 
335  if ( currentProperty )
336  {
337  continue;
338  }
339  else // QgsProjectPropertyValue not Key, so return null
340  {
341  return;
342  }
343  }
344  else // if the next key down isn't found
345  {
346  // then the overall key sequence doesn't exist
347  return;
348  }
349  }
350  else
351  {
352  return;
353  }
354  }
355 }
356 
357 QgsProject::QgsProject( QObject *parent )
358  : QObject( parent )
359  , mLayerStore( new QgsMapLayerStore( this ) )
360  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
361  , mSnappingConfig( this )
362  , mRelationManager( new QgsRelationManager( this ) )
363  , mAnnotationManager( new QgsAnnotationManager( this ) )
364  , mLayoutManager( new QgsLayoutManager( this ) )
365  , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
366  , mViewSettings( new QgsProjectViewSettings( this ) )
367  , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
368  , mRootGroup( new QgsLayerTree )
369  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
370  , mArchive( new QgsProjectArchive() )
371  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
372 {
373  mProperties.setName( QStringLiteral( "properties" ) );
374  clear();
375 
376  // bind the layer tree to the map layer registry.
377  // whenever layers are added to or removed from the registry,
378  // layer tree will be updated
379  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
380  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
381  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
382  connect( this, qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
383 
384  // proxy map layer store signals to this
385  connect( mLayerStore.get(), qgis::overload<const QStringList &>::of( &QgsMapLayerStore::layersWillBeRemoved ),
386  this, qgis::overload< const QStringList &>::of( &QgsProject::layersWillBeRemoved ) );
387  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ),
388  this, qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsProject::layersWillBeRemoved ) );
389  connect( mLayerStore.get(), qgis::overload< const QString & >::of( &QgsMapLayerStore::layerWillBeRemoved ),
390  this, qgis::overload< const QString & >::of( &QgsProject::layerWillBeRemoved ) );
391  connect( mLayerStore.get(), qgis::overload< QgsMapLayer * >::of( &QgsMapLayerStore::layerWillBeRemoved ),
392  this, qgis::overload< QgsMapLayer * >::of( &QgsProject::layerWillBeRemoved ) );
393  connect( mLayerStore.get(), qgis::overload<const QStringList & >::of( &QgsMapLayerStore::layersRemoved ), this, &QgsProject::layersRemoved );
394  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this, &QgsProject::layerRemoved );
395  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this, &QgsProject::removeAll );
396  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this, &QgsProject::layersAdded );
397  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this, &QgsProject::layerWasAdded );
398 
399  if ( QgsApplication::instance() )
400  {
402  }
403 
404  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ), this,
405  [ = ]( const QList<QgsMapLayer *> &layers )
406  {
407  for ( const auto &layer : layers )
408  {
409  disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
410  }
411  }
412  );
413  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersAdded ), this,
414  [ = ]( const QList<QgsMapLayer *> &layers )
415  {
416  for ( const auto &layer : layers )
417  {
418  connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
419  }
420  }
421  );
422 
426 }
427 
428 
430 {
431  mIsBeingDeleted = true;
432 
433  clear();
434  delete mBadLayerHandler;
435  delete mRelationManager;
436  delete mLayerTreeRegistryBridge;
437  delete mRootGroup;
438  if ( this == sProject )
439  {
440  sProject = nullptr;
441  }
442 }
443 
444 void QgsProject::setInstance( QgsProject *project )
445 {
446  sProject = project;
447 }
448 
449 
451 {
452  if ( !sProject )
453  {
454  sProject = new QgsProject;
455  }
456  return sProject;
457 }
458 
459 void QgsProject::setTitle( const QString &title )
460 {
461  if ( title == mMetadata.title() )
462  return;
463 
464  mMetadata.setTitle( title );
465  mProjectScope.reset();
466  emit metadataChanged();
467 
468  setDirty( true );
469 }
470 
471 QString QgsProject::title() const
472 {
473  return mMetadata.title();
474 }
475 
476 QString QgsProject::saveUser() const
477 {
478  return mSaveUser;
479 }
480 
482 {
483  return mSaveUserFull;
484 }
485 
487 {
488  return mDirty;
489 }
490 
491 void QgsProject::setDirty( const bool dirty )
492 {
493  if ( dirty && mDirtyBlockCount > 0 )
494  return;
495 
496  if ( mDirty == dirty )
497  return;
498 
499  mDirty = dirty;
500  emit isDirtyChanged( mDirty );
501 }
502 
503 void QgsProject::setPresetHomePath( const QString &path )
504 {
505  if ( path == mHomePath )
506  return;
507 
508  mHomePath = path;
509  mCachedHomePath.clear();
510  mProjectScope.reset();
511 
512  emit homePathChanged();
513 
514  setDirty( true );
515 }
516 
517 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
518 {
519  const QList<QgsAttributeEditorElement *> elements = parent->children();
520 
521  for ( QgsAttributeEditorElement *element : elements )
522  {
523  if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
524  {
525  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
526 
527  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
528 
529  if ( !container->children().empty() )
530  registerTranslatableContainers( translationContext, container, layerId );
531  }
532  }
533 }
534 
536 {
537  //register layers
538  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
539 
540  for ( const QgsLayerTreeLayer *layer : layers )
541  {
542  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
543 
544  QgsMapLayer *mapLayer = layer->layer();
545  if ( mapLayer && mapLayer->type() == QgsMapLayerType::VectorLayer )
546  {
547  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
548 
549  //register aliases and fields
550  const QgsFields fields = vlayer->fields();
551  for ( const QgsField &field : fields )
552  {
553  QString fieldName;
554  if ( field.alias().isEmpty() )
555  fieldName = field.name();
556  else
557  fieldName = field.alias();
558 
559  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
560 
561  if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueRelation" ) )
562  {
563  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
564  }
565  }
566 
567  //register formcontainers
568  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
569 
570  }
571  }
572 
573  //register layergroups
574  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
575  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
576  {
577  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
578  }
579 
580  //register relations
581  const QList<QgsRelation> &relations = mRelationManager->relations().values();
582  for ( const QgsRelation &relation : relations )
583  {
584  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
585  }
586 }
587 
588 void QgsProject::setFileName( const QString &name )
589 {
590  if ( name == mFile.fileName() )
591  return;
592 
593  QString oldHomePath = homePath();
594 
595  mFile.setFileName( name );
596  mCachedHomePath.clear();
597  mProjectScope.reset();
598 
599  emit fileNameChanged();
600 
601  QString newHomePath = homePath();
602  if ( newHomePath != oldHomePath )
603  emit homePathChanged();
604 
605  setDirty( true );
606 }
607 
608 QString QgsProject::fileName() const
609 {
610  return mFile.fileName();
611 }
612 
613 QFileInfo QgsProject::fileInfo() const
614 {
615  return QFileInfo( mFile );
616 }
617 
619 {
621 }
622 
623 QDateTime QgsProject::lastModified() const
624 {
625  if ( QgsProjectStorage *storage = projectStorage() )
626  {
628  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
629  return metadata.lastModified;
630  }
631  else
632  {
633  return QFileInfo( mFile.fileName() ).lastModified();
634  }
635 }
636 
638 {
639  if ( projectStorage() )
640  return QString();
641 
642  if ( mFile.fileName().isEmpty() )
643  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
644 
645  return QFileInfo( mFile.fileName() ).absolutePath();
646 }
647 
649 {
650  if ( projectStorage() )
651  return QString();
652 
653  if ( mFile.fileName().isEmpty() )
654  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
655 
656  return QFileInfo( mFile.fileName() ).absoluteFilePath();
657 }
658 
659 QString QgsProject::baseName() const
660 {
661  if ( QgsProjectStorage *storage = projectStorage() )
662  {
664  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
665  return metadata.name;
666  }
667  else
668  {
669  return QFileInfo( mFile.fileName() ).completeBaseName();
670  }
671 }
672 
674 {
675  return mCrs;
676 }
677 
678 void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
679 {
680  if ( crs != mCrs )
681  {
682  mCrs = crs;
683  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
684  mProjectScope.reset();
685  setDirty( true );
686  emit crsChanged();
687  }
688 
689  if ( adjustEllipsoid )
691 }
692 
693 QString QgsProject::ellipsoid() const
694 {
695  if ( !crs().isValid() )
696  return geoNone();
697 
698  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), geoNone() );
699 }
700 
701 void QgsProject::setEllipsoid( const QString &ellipsoid )
702 {
703  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
704  return;
705 
706  mProjectScope.reset();
707  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
708  emit ellipsoidChanged( ellipsoid );
709 }
710 
712 {
713  return mTransformContext;
714 }
715 
717 {
718  if ( context == mTransformContext )
719  return;
720 
721  mTransformContext = context;
722  mProjectScope.reset();
723 
724  for ( auto &layer : mLayerStore.get()->mapLayers() )
725  {
726  layer->setTransformContext( context );
727  }
729 }
730 
732 {
733  QgsSettings settings;
734 
735  mProjectScope.reset();
736  mFile.setFileName( QString() );
737  mProperties.clearKeys();
738  mSaveUser.clear();
739  mSaveUserFull.clear();
740  mHomePath.clear();
741  mCachedHomePath.clear();
742  mAutoTransaction = false;
743  mEvaluateDefaultValues = false;
744  mDirty = false;
745  mTrustLayerMetadata = false;
746  mCustomVariables.clear();
747  mMetadata = QgsProjectMetadata();
748  if ( !settings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
749  {
750  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
752  }
753  emit metadataChanged();
754 
756  context.readSettings();
757  setTransformContext( context );
758 
759  mEmbeddedLayers.clear();
760  mRelationManager->clear();
761  mAnnotationManager->clear();
762  mLayoutManager->clear();
763  mBookmarkManager->clear();
764  mViewSettings->reset();
765  mDisplaySettings->reset();
766  mSnappingConfig.reset();
767  emit snappingConfigChanged( mSnappingConfig );
769 
770  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
772 
773  mLabelingEngineSettings->clear();
774 
775  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
776  mArchive->clear();
777 
779 
780  if ( !mIsBeingDeleted )
781  {
782  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
783  emit projectColorsChanged();
784  }
785 
786  // reset some default project properties
787  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
788  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
789  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
790  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
791 
792  //copy default units to project
793  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), settings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
794  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), settings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
795 
796  int red = settings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
797  int green = settings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
798  int blue = settings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
799  setBackgroundColor( QColor( red, green, blue ) );
800 
801  red = settings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
802  green = settings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
803  blue = settings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
804  int alpha = settings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
805  setSelectionColor( QColor( red, green, blue, alpha ) );
806 
808  mRootGroup->clear();
809 
810  setDirty( false );
811  emit homePathChanged();
812  emit cleared();
813 }
814 
815 // basically a debugging tool to dump property list values
816 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
817 {
818  QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
819  topQgsPropertyKey.dump();
820 }
821 
822 
853 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
854 {
855  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
856 
857  if ( propertiesElem.isNull() ) // no properties found, so we're done
858  {
859  return;
860  }
861 
862  QDomNodeList scopes = propertiesElem.childNodes();
863 
864  if ( scopes.count() < 1 )
865  {
866  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
867  return;
868  }
869 
870  if ( ! project_properties.readXml( propertiesElem ) )
871  {
872  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
873  }
874 }
875 
876 
881 static void _getTitle( const QDomDocument &doc, QString &title )
882 {
883  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
884 
885  title.clear(); // by default the title will be empty
886 
887  if ( !nl.count() )
888  {
889  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
890  return;
891  }
892 
893  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element OK
894 
895  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
896  {
897  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
898  return;
899  }
900 
901  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
902 
903  if ( !titleTextNode.isText() )
904  {
905  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
906  return;
907  }
908 
909  QDomText titleText = titleTextNode.toText();
910 
911  title = titleText.data();
912 
913 }
914 
915 static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull )
916 {
917  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
918 
919  if ( !nl.count() )
920  {
921  QgsDebugMsg( "unable to find qgis element" );
922  return;
923  }
924 
925  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
926 
927  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
928  lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
929  lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
930 }
931 
932 
933 QgsProjectVersion getVersion( const QDomDocument &doc )
934 {
935  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
936 
937  if ( !nl.count() )
938  {
939  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
940  return QgsProjectVersion( 0, 0, 0, QString() );
941  }
942 
943  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
944 
945  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
946  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
947  return projectVersion;
948 }
949 
950 
952 {
953  return mSnappingConfig;
954 }
955 
957 {
958  if ( mSnappingConfig == snappingConfig )
959  return;
960 
961  mSnappingConfig = snappingConfig;
962  setDirty( true );
963  emit snappingConfigChanged( mSnappingConfig );
964 }
965 
966 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, QgsProject::ReadFlags flags )
967 {
968  // Layer order is set by the restoring the legend settings from project file.
969  // This is done on the 'readProject( ... )' signal
970 
971  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
972 
973  // process the map layer nodes
974 
975  if ( 0 == nl.count() ) // if we have no layers to process, bail
976  {
977  return true; // Decided to return "true" since it's
978  // possible for there to be a project with no
979  // layers; but also, more imporantly, this
980  // would cause the tests/qgsproject to fail
981  // since the test suite doesn't currently
982  // support test layers
983  }
984 
985  bool returnStatus = true;
986 
987  emit layerLoaded( 0, nl.count() );
988 
989  // order layers based on their dependencies
990  QgsLayerDefinition::DependencySorter depSorter( doc );
991  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
992  return false;
993 
994  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
995  const int totalLayerCount = sortedLayerNodes.count();
996 
997  int i = 0;
998  for ( const QDomNode &node : sortedLayerNodes )
999  {
1000  const QDomElement element = node.toElement();
1001 
1002  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1003  if ( !name.isNull() )
1004  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1005 
1006  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1007  {
1008  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1009  }
1010  else
1011  {
1012  QgsReadWriteContext context;
1013  context.setPathResolver( pathResolver() );
1014  context.setProjectTranslator( this );
1016 
1017  if ( !addLayer( element, brokenNodes, context, flags ) )
1018  {
1019  returnStatus = false;
1020  }
1021  const auto messages = context.takeMessages();
1022  if ( !messages.isEmpty() )
1023  {
1024  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1025  }
1026  }
1027  emit layerLoaded( i + 1, totalLayerCount );
1028  i++;
1029  }
1030 
1031  return returnStatus;
1032 }
1033 
1034 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, QgsProject::ReadFlags flags )
1035 {
1036  QString type = layerElem.attribute( QStringLiteral( "type" ) );
1037  QgsDebugMsgLevel( "Layer type is " + type, 4 );
1038  std::unique_ptr<QgsMapLayer> mapLayer;
1039 
1040  if ( type == QLatin1String( "vector" ) )
1041  {
1042  mapLayer = qgis::make_unique<QgsVectorLayer>();
1043  // apply specific settings to vector layer
1044  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1045  {
1046  vl->setReadExtentFromXml( mTrustLayerMetadata );
1047  }
1048  }
1049  else if ( type == QLatin1String( "raster" ) )
1050  {
1051  mapLayer = qgis::make_unique<QgsRasterLayer>();
1052  }
1053  else if ( type == QLatin1String( "mesh" ) )
1054  {
1055  mapLayer = qgis::make_unique<QgsMeshLayer>();
1056  }
1057  else if ( type == QLatin1String( "plugin" ) )
1058  {
1059  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1060  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1061  }
1062 
1063  if ( !mapLayer )
1064  {
1065  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1066  return false;
1067  }
1068 
1069  Q_CHECK_PTR( mapLayer ); // NOLINT
1070 
1071  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1072  // because if it was, the newly created layer will not be added to the store and it would leak.
1073  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1074  Q_ASSERT( ! layerId.isEmpty() );
1075  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1076 
1077  // have the layer restore state that is stored in Dom node
1078  QgsMapLayer::ReadFlags layerFlags = nullptr;
1080  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1081  bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1082  QList<QgsMapLayer *> newLayers;
1083  newLayers << mapLayer.get();
1084  if ( layerIsValid || flags & QgsProject::ReadFlag::FlagDontResolveLayers )
1085  {
1086  emit readMapLayer( mapLayer.get(), layerElem );
1087  addMapLayers( newLayers );
1088  }
1089  else
1090  {
1091  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1092  addMapLayers( newLayers, false );
1093  newLayers.first();
1094  QgsDebugMsg( "Unable to load " + type + " layer" );
1095  brokenNodes.push_back( layerElem );
1096  }
1097 
1098  // It should be safe to delete the layer now if layer was stored, because all the store
1099  // had to to was to reset the data source in case the validity changed.
1100  if ( ! layerWasStored )
1101  {
1102  mapLayer.release();
1103  }
1104 
1105  return layerIsValid;
1106 }
1107 
1108 bool QgsProject::read( const QString &filename, QgsProject::ReadFlags flags )
1109 {
1110  mFile.setFileName( filename );
1111  mCachedHomePath.clear();
1112  mProjectScope.reset();
1113 
1114  return read( flags );
1115 }
1116 
1117 bool QgsProject::read( QgsProject::ReadFlags flags )
1118 {
1119  QString filename = mFile.fileName();
1120  bool returnValue;
1121 
1122  if ( QgsProjectStorage *storage = projectStorage() )
1123  {
1124  QTemporaryFile inDevice;
1125  if ( !inDevice.open() )
1126  {
1127  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1128  return false;
1129  }
1130 
1131  QgsReadWriteContext context;
1132  context.setProjectTranslator( this );
1133  if ( !storage->readProject( filename, &inDevice, context ) )
1134  {
1135  QString err = tr( "Unable to open %1" ).arg( filename );
1136  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1137  if ( !messages.isEmpty() )
1138  err += QStringLiteral( "\n\n" ) + messages.last().message();
1139  setError( err );
1140  return false;
1141  }
1142  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1143  }
1144  else
1145  {
1146  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1147  {
1148  returnValue = unzip( mFile.fileName(), flags );
1149  }
1150  else
1151  {
1152  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1153  returnValue = readProjectFile( mFile.fileName(), flags );
1154  }
1155 
1156  //on translation we should not change the filename back
1157  if ( !mTranslator )
1158  {
1159  mFile.setFileName( filename );
1160  mCachedHomePath.clear();
1161  mProjectScope.reset();
1162  }
1163  else
1164  {
1165  //but delete the translator
1166  mTranslator.reset( nullptr );
1167  }
1168  }
1169  emit homePathChanged();
1170  return returnValue;
1171 }
1172 
1173 bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags flags )
1174 {
1175  QFile projectFile( filename );
1176  clearError();
1177 
1178  QgsSettings settings;
1179 
1180  QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), settings.value( QStringLiteral( "locale/userLocale" ), QString() ).toString() );
1181 
1182  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1183  {
1184  mTranslator.reset( new QTranslator() );
1185  mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1186  }
1187 
1188  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1189 
1190  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1191  {
1192  projectFile.close();
1193 
1194  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1195 
1196  return false;
1197  }
1198 
1199  // location of problem associated with errorMsg
1200  int line, column;
1201  QString errorMsg;
1202 
1203  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1204  {
1205  // want to make this class as GUI independent as possible; so commented out
1206 #if 0
1207  QMessageBox::critical( 0, tr( "Read Project File" ),
1208  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
1209 #endif
1210 
1211  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1212  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1213 
1214  QgsDebugMsg( errorString );
1215 
1216  projectFile.close();
1217 
1218  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1219 
1220  return false;
1221  }
1222 
1223  projectFile.close();
1224 
1225  QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1226 
1227  // get project version string, if any
1228  QgsProjectVersion fileVersion = getVersion( *doc );
1229  QgsProjectVersion thisVersion( Qgis::version() );
1230 
1231  if ( thisVersion > fileVersion )
1232  {
1233  QgsLogger::warning( "Loading a file that was saved with an older "
1234  "version of qgis (saved in " + fileVersion.text() +
1235  ", loaded in " + Qgis::version() +
1236  "). Problems may occur." );
1237 
1238  QgsProjectFileTransform projectFile( *doc, fileVersion );
1239 
1240  // Shows a warning when an old project file is read.
1241  emit oldProjectVersionWarning( fileVersion.text() );
1242 
1243  projectFile.updateRevision( thisVersion );
1244  }
1245 
1246  // start new project, just keep the file name and auxiliary storage
1247  QString fileName = mFile.fileName();
1248  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1249  clear();
1250  mAuxiliaryStorage = std::move( aStorage );
1251  mFile.setFileName( fileName );
1252  mCachedHomePath.clear();
1253  mProjectScope.reset();
1254 
1255  // now get any properties
1256  _getProperties( *doc, mProperties );
1257 
1258  QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1259 
1260 #if 0
1261  dump_( mProperties );
1262 #endif
1263 
1264  // get older style project title
1265  QString oldTitle;
1266  _getTitle( *doc, oldTitle );
1267 
1268  readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull );
1269 
1270  QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1271  if ( homePathNl.count() > 0 )
1272  {
1273  QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1274  QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1275  if ( !homePath.isEmpty() )
1276  setPresetHomePath( homePath );
1277  }
1278  else
1279  {
1280  emit homePathChanged();
1281  }
1282 
1283  const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1284  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1285  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1286  setBackgroundColor( backgroundColor );
1287  const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1288  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1289  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1290  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1291  setSelectionColor( selectionColor );
1292 
1293  QgsReadWriteContext context;
1294  context.setPathResolver( pathResolver() );
1295  context.setProjectTranslator( this );
1296 
1297  //crs
1298  QgsCoordinateReferenceSystem projectCrs;
1299  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1300  {
1301  // first preference - dedicated projectCrs node
1302  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1303  if ( !srsNode.isNull() )
1304  {
1305  projectCrs.readXml( srsNode );
1306  }
1307 
1308  if ( !projectCrs.isValid() )
1309  {
1310  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1311  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1312  const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1313 
1314  // authid should be prioritized over all
1315  bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
1316  if ( !authid.isEmpty() && !isUserAuthId )
1317  projectCrs = QgsCoordinateReferenceSystem( authid );
1318 
1319  // try the CRS
1320  if ( !projectCrs.isValid() && currentCRS >= 0 )
1321  {
1322  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1323  }
1324 
1325  // if that didn't produce a match, try the proj.4 string
1326  if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
1327  {
1328  projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
1329  }
1330 
1331  // last just take the given id
1332  if ( !projectCrs.isValid() )
1333  {
1334  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1335  }
1336  }
1337  }
1338  mCrs = projectCrs;
1339 
1340  QStringList datumErrors;
1341  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1342  {
1343  emit missingDatumTransforms( datumErrors );
1344  }
1345  emit transformContextChanged();
1346 
1347  //add variables defined in project file - do this early in the reading cycle, as other components
1348  //(e.g. layouts) may depend on these variables
1349  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1350  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1351 
1352  mCustomVariables.clear();
1353  if ( variableNames.length() == variableValues.length() )
1354  {
1355  for ( int i = 0; i < variableNames.length(); ++i )
1356  {
1357  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1358  }
1359  }
1360  else
1361  {
1362  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1363  }
1364 
1365  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "projectMetadata" ) );
1366  if ( !nl.isEmpty() )
1367  {
1368  QDomElement metadataElement = nl.at( 0 ).toElement();
1369  mMetadata.readMetadataXml( metadataElement );
1370  }
1371  else
1372  {
1373  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1374  mMetadata = QgsProjectMetadata();
1375  }
1376  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1377  {
1378  // upgrade older title storage to storing within project metadata.
1379  mMetadata.setTitle( oldTitle );
1380  }
1381  emit metadataChanged();
1382 
1383  nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
1384  if ( nl.count() )
1385  {
1386  QDomElement transactionElement = nl.at( 0 ).toElement();
1387  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1388  mAutoTransaction = true;
1389  }
1390 
1391  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
1392  if ( nl.count() )
1393  {
1394  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
1395  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1396  mEvaluateDefaultValues = true;
1397  }
1398 
1399  nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
1400  if ( nl.count() )
1401  {
1402  QDomElement trustElement = nl.at( 0 ).toElement();
1403  if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1404  mTrustLayerMetadata = true;
1405  }
1406 
1407  // read the layer tree from project file
1408 
1409  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1410 
1411  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1412  if ( !layerTreeElem.isNull() )
1413  {
1414  mRootGroup->readChildrenFromXml( layerTreeElem, context );
1415  }
1416  else
1417  {
1418  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1419  }
1420 
1421  mLayerTreeRegistryBridge->setEnabled( false );
1422 
1423  // get the map layers
1424  QList<QDomNode> brokenNodes;
1425  bool clean = _getMapLayers( *doc, brokenNodes, flags );
1426 
1427  // review the integrity of the retrieved map layers
1428  if ( !clean )
1429  {
1430  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1431 
1432  if ( !brokenNodes.isEmpty() )
1433  {
1434  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1435  }
1436 
1437  // we let a custom handler decide what to do with missing layers
1438  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1439  mBadLayerHandler->handleBadLayers( brokenNodes );
1440  }
1441 
1442  // Resolve references to other layers
1443  // Needs to be done here once all dependent layers are loaded
1444  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1445  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
1446  {
1447  it.value()->resolveReferences( this );
1448  }
1449 
1450  mLayerTreeRegistryBridge->setEnabled( true );
1451 
1452  // load embedded groups and layers
1453  loadEmbeddedNodes( mRootGroup, flags );
1454 
1455  // now that layers are loaded, we can resolve layer tree's references to the layers
1456  mRootGroup->resolveReferences( this );
1457 
1458 
1459  if ( !layerTreeElem.isNull() )
1460  {
1461  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1462  }
1463 
1464  // Load pre 3.0 configuration
1465  QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1466  if ( !layerTreeCanvasElem.isNull( ) )
1467  {
1468  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1469  }
1470 
1471  // Convert pre 3.4 to create layers flags
1472  if ( QgsProjectVersion( 3, 4, 0 ) > fileVersion )
1473  {
1474  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1475  for ( const QString &layerId : requiredLayerIds )
1476  {
1477  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1478  {
1479  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1480  }
1481  }
1482  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1483  for ( const QString &layerId : disabledLayerIds )
1484  {
1485  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1486  {
1487  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1488  }
1489  }
1490  }
1491 
1492  // After bad layer handling we might still have invalid layers,
1493  // store them in case the user wanted to handle them later
1494  // or wanted to pass them through when saving
1495  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1496 
1497  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1498 
1499  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1501  mMapThemeCollection->readXml( *doc );
1502 
1503  mLabelingEngineSettings->readSettingsFromProject( this );
1505 
1506  mAnnotationManager->readXml( doc->documentElement(), context );
1507  if ( !( flags & QgsProject::ReadFlag::FlagDontLoadLayouts ) )
1508  mLayoutManager->readXml( doc->documentElement(), *doc );
1509  mBookmarkManager->readXml( doc->documentElement(), *doc );
1510 
1511  // reassign change dependencies now that all layers are loaded
1512  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1513  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1514  {
1515  it.value()->setDependencies( it.value()->dependencies() );
1516  }
1517 
1518  mSnappingConfig.readProject( *doc );
1519 
1520  // restore older project scales settings
1521  mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
1522  const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
1523  QVector<double> res;
1524  for ( const QString &scale : scales )
1525  {
1526  const QStringList parts = scale.split( ':' );
1527  if ( parts.size() != 2 )
1528  continue;
1529 
1530  bool ok = false;
1531  const double denominator = QLocale().toDouble( parts[1], &ok );
1532  if ( ok )
1533  {
1534  res << denominator;
1535  }
1536  }
1537  mViewSettings->setMapScales( res );
1538  QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
1539  if ( !viewSettingsElement.isNull() )
1540  mViewSettings->readXml( viewSettingsElement, context );
1541 
1542  QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
1543  if ( !displaySettingsElement.isNull() )
1544  mDisplaySettings->readXml( displaySettingsElement, context );
1545 
1546  emit customVariablesChanged();
1547  emit crsChanged();
1548  emit ellipsoidChanged( ellipsoid() );
1549 
1550  // read the project: used by map canvas and legend
1551  emit readProject( *doc );
1552  emit readProjectWithContext( *doc, context );
1553  emit snappingConfigChanged( mSnappingConfig );
1555  emit projectColorsChanged();
1556 
1557  // if all went well, we're allegedly in pristine state
1558  if ( clean )
1559  setDirty( false );
1560 
1561  QgsDebugMsg( QString( "Project save user: %1" ).arg( mSaveUser ) );
1562  QgsDebugMsg( QString( "Project save user: %1" ).arg( mSaveUserFull ) );
1563 
1567 
1568  if ( mTranslator )
1569  {
1570  //project possibly translated -> rename it with locale postfix
1571  QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
1572  setFileName( newFileName );
1573 
1574  if ( write() )
1575  {
1576  setTitle( localeFileName );
1577  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Success );
1578  }
1579  else
1580  {
1581  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Critical );
1582  }
1583  }
1584  return true;
1585 }
1586 
1587 
1588 bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
1589 {
1590  bool valid = true;
1591  const auto constChildren = group->children();
1592  for ( QgsLayerTreeNode *child : constChildren )
1593  {
1594  if ( QgsLayerTree::isGroup( child ) )
1595  {
1596  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1597  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1598  {
1599  // make sure to convert the path from relative to absolute
1600  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1601  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1602  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
1603  if ( newGroup )
1604  {
1605  QList<QgsLayerTreeNode *> clonedChildren;
1606  const auto constChildren = newGroup->children();
1607  for ( QgsLayerTreeNode *newGroupChild : constChildren )
1608  clonedChildren << newGroupChild->clone();
1609  delete newGroup;
1610 
1611  childGroup->insertChildNodes( 0, clonedChildren );
1612  }
1613  }
1614  else
1615  {
1616  loadEmbeddedNodes( childGroup, flags );
1617  }
1618  }
1619  else if ( QgsLayerTree::isLayer( child ) )
1620  {
1621  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1622  {
1623  QList<QDomNode> brokenNodes;
1624  if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
1625  {
1626  valid = valid && false;
1627  }
1628  }
1629  }
1630 
1631  }
1632 
1633  return valid;
1634 }
1635 
1636 QVariantMap QgsProject::customVariables() const
1637 {
1638  return mCustomVariables;
1639 }
1640 
1641 void QgsProject::setCustomVariables( const QVariantMap &variables )
1642 {
1643  if ( variables == mCustomVariables )
1644  return;
1645 
1646  //write variable to project
1647  QStringList variableNames;
1648  QStringList variableValues;
1649 
1650  QVariantMap::const_iterator it = variables.constBegin();
1651  for ( ; it != variables.constEnd(); ++it )
1652  {
1653  variableNames << it.key();
1654  variableValues << it.value().toString();
1655  }
1656 
1657  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1658  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1659 
1660  mCustomVariables = variables;
1661  mProjectScope.reset();
1662 
1663  emit customVariablesChanged();
1664 }
1665 
1667 {
1668  *mLabelingEngineSettings = settings;
1670 }
1671 
1673 {
1674  return *mLabelingEngineSettings;
1675 }
1676 
1678 {
1679  return mLayerStore.get();
1680 }
1681 
1683 {
1684  return mLayerStore.get();
1685 }
1686 
1687 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1688 {
1689  QList<QgsVectorLayer *> layers;
1690  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1691  const auto constLayerIds = layerIds;
1692  for ( const QString &layerId : constLayerIds )
1693  {
1694  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1695  layers << vlayer;
1696  }
1697  return layers;
1698 }
1699 
1700 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1701 {
1702  QStringList list;
1703  const auto constLayers = layers;
1704  for ( QgsVectorLayer *layer : constLayers )
1705  list << layer->id();
1706  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1708 }
1709 
1711 {
1712  QgsExpressionContext context;
1713 
1716 
1717  return context;
1718 }
1719 
1721 {
1722  // MUCH cheaper to clone than build
1723  if ( mProjectScope )
1724  {
1725  std::unique_ptr< QgsExpressionContextScope > projectScope = qgis::make_unique< QgsExpressionContextScope >( *mProjectScope );
1726  // we can't cache these
1727  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
1728  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
1729  return projectScope.release();
1730  }
1731 
1732  mProjectScope = qgis::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
1733 
1734  const QVariantMap vars = customVariables();
1735 
1736  QVariantMap::const_iterator it = vars.constBegin();
1737 
1738  for ( ; it != vars.constEnd(); ++it )
1739  {
1740  mProjectScope->setVariable( it.key(), it.value(), true );
1741  }
1742 
1743  QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
1744  QString projectFolder = QFileInfo( projectPath ).path();
1745  QString projectFilename = QFileInfo( projectPath ).fileName();
1746  QString projectBasename = baseName();
1747 
1748  //add other known project variables
1749  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
1750  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
1751  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
1752  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
1753  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
1754  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
1755  QgsCoordinateReferenceSystem projectCrs = crs();
1756  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
1757  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
1758  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
1759  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
1760 
1761  // metadata
1762  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
1763  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
1764  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
1765  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
1766 
1767  // keywords
1768  QVariantMap keywords;
1769  QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
1770  for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
1771  {
1772  keywords.insert( it.key(), it.value() );
1773  }
1774  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
1775 
1776  mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
1777 
1779 }
1780 
1781 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1782 {
1783  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1784 
1785  bool tgChanged = false;
1786 
1787  const auto constLayers = layers;
1788  for ( QgsMapLayer *layer : constLayers )
1789  {
1790  if ( layer->isValid() )
1791  {
1792  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1793  if ( vlayer )
1794  {
1795  if ( autoTransaction() )
1796  {
1797  if ( QgsTransaction::supportsTransaction( vlayer ) )
1798  {
1799  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1800  QString key = vlayer->providerType();
1801 
1802  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1803 
1804  if ( !tg )
1805  {
1806  tg = new QgsTransactionGroup();
1807  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1808  tgChanged = true;
1809  }
1810  tg->addLayer( vlayer );
1811  }
1812  }
1814  }
1815 
1816  if ( tgChanged )
1817  emit transactionGroupsChanged();
1818 
1819  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1820 
1821  // check if we have to update connections for layers with dependencies
1822  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1823  {
1824  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1825  if ( deps.contains( layer->id() ) )
1826  {
1827  // reconnect to change signals
1828  it.value()->setDependencies( deps );
1829  }
1830  }
1831  }
1832  }
1833 
1834  if ( mSnappingConfig.addLayers( layers ) )
1835  emit snappingConfigChanged( mSnappingConfig );
1836 }
1837 
1838 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1839 {
1840  if ( mSnappingConfig.removeLayers( layers ) )
1841  emit snappingConfigChanged( mSnappingConfig );
1842 }
1843 
1844 void QgsProject::cleanTransactionGroups( bool force )
1845 {
1846  bool changed = false;
1847  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1848  {
1849  if ( tg.value()->isEmpty() || force )
1850  {
1851  delete tg.value();
1852  tg = mTransactionGroups.erase( tg );
1853  changed = true;
1854  }
1855  else
1856  {
1857  ++tg;
1858  }
1859  }
1860  if ( changed )
1861  emit transactionGroupsChanged();
1862 }
1863 
1864 bool QgsProject::readLayer( const QDomNode &layerNode )
1865 {
1866  QgsReadWriteContext context;
1867  context.setPathResolver( pathResolver() );
1868  context.setProjectTranslator( this );
1870  QList<QDomNode> brokenNodes;
1871  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
1872  {
1873  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1874  // added layer for joins
1875  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1876  const auto constVectorLayers = vectorLayers;
1877  for ( QgsVectorLayer *layer : constVectorLayers )
1878  {
1879  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1880  layer->resolveReferences( this );
1881  }
1882 
1883  return true;
1884  }
1885  return false;
1886 }
1887 
1888 bool QgsProject::write( const QString &filename )
1889 {
1890  mFile.setFileName( filename );
1891  mCachedHomePath.clear();
1892  mProjectScope.reset();
1893 
1894  return write();
1895 }
1896 
1898 {
1899  if ( QgsProjectStorage *storage = projectStorage() )
1900  {
1901  QgsReadWriteContext context;
1902  // for projects stored in a custom storage, we have to check for the support
1903  // of relative paths since the storage most likely will not be in a file system
1904  QString storageFilePath { storage->filePath( mFile.fileName() ) };
1905  if ( storageFilePath.isEmpty() )
1906  {
1907  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
1908  }
1909  context.setPathResolver( pathResolver() );
1910 
1911  QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
1912  QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
1913 
1914  if ( !zip( tmpZipFilename ) )
1915  return false; // zip() already calls setError() when returning false
1916 
1917  QFile tmpZipFile( tmpZipFilename );
1918  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
1919  {
1920  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
1921  return false;
1922  }
1923 
1925  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
1926  {
1927  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
1928  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1929  if ( !messages.isEmpty() )
1930  err += QStringLiteral( "\n\n" ) + messages.last().message();
1931  setError( err );
1932  return false;
1933  }
1934 
1935  tmpZipFile.close();
1936  QFile::remove( tmpZipFilename );
1937 
1938  return true;
1939  }
1940 
1941  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1942  {
1943  return zip( mFile.fileName() );
1944  }
1945  else
1946  {
1947  // write project file even if the auxiliary storage is not correctly
1948  // saved
1949  const bool asOk = saveAuxiliaryStorage();
1950  const bool writeOk = writeProjectFile( mFile.fileName() );
1951 
1952  // errors raised during writing project file are more important
1953  if ( !asOk && writeOk )
1954  {
1955  const QString err = mAuxiliaryStorage->errorString();
1956  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
1957  }
1958 
1959  return asOk && writeOk;
1960  }
1961 }
1962 
1963 bool QgsProject::writeProjectFile( const QString &filename )
1964 {
1965  QFile projectFile( filename );
1966  clearError();
1967 
1968  // if we have problems creating or otherwise writing to the project file,
1969  // let's find out up front before we go through all the hand-waving
1970  // necessary to create all the Dom objects
1971  QFileInfo myFileInfo( projectFile );
1972  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1973  {
1974  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1975  .arg( projectFile.fileName() ) );
1976  return false;
1977  }
1978 
1979  QgsReadWriteContext context;
1980  context.setPathResolver( pathResolver() );
1982 
1983  QDomImplementation DomImplementation;
1984  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1985 
1986  QDomDocumentType documentType =
1987  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1988  QStringLiteral( "SYSTEM" ) );
1989  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1990 
1991  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1992  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1993  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::version() ) );
1994 
1995  QgsSettings settings;
1996  if ( !settings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
1997  {
1998  QString newSaveUser = QgsApplication::userLoginName();
1999  QString newSaveUserFull = QgsApplication::userFullName();
2000  qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2001  qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2002  mSaveUser = newSaveUser;
2003  mSaveUserFull = newSaveUserFull;
2004  }
2005  else
2006  {
2007  mSaveUser.clear();
2008  mSaveUserFull.clear();
2009  }
2010  doc->appendChild( qgisNode );
2011 
2012  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2013  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2014  qgisNode.appendChild( homePathNode );
2015 
2016  // title
2017  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2018  qgisNode.appendChild( titleNode );
2019 
2020  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
2021  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? 1 : 0 );
2022  qgisNode.appendChild( transactionNode );
2023 
2024  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
2025  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? 1 : 0 );
2026  qgisNode.appendChild( evaluateDefaultValuesNode );
2027 
2028  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
2029  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? 1 : 0 );
2030  qgisNode.appendChild( trustNode );
2031 
2032  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
2033  titleNode.appendChild( titleText );
2034 
2035  // write project CRS
2036  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
2037  mCrs.writeXml( srsNode, *doc );
2038  qgisNode.appendChild( srsNode );
2039 
2040  // write layer tree - make sure it is without embedded subgroups
2041  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
2043  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
2044 
2045  clonedRoot->writeXml( qgisNode, context );
2046  delete clonedRoot;
2047 
2048  mSnappingConfig.writeProject( *doc );
2049 
2050  // let map canvas and legend write their information
2051  emit writeProject( *doc );
2052 
2053  // within top level node save list of layers
2054  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2055 
2056  // Iterate over layers in zOrder
2057  // Call writeXml() on each
2058  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
2059 
2060  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
2061  while ( li != layers.end() )
2062  {
2063  QgsMapLayer *ml = li.value();
2064 
2065  if ( ml )
2066  {
2067  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
2068  if ( emIt == mEmbeddedLayers.constEnd() )
2069  {
2070  QDomElement maplayerElem;
2071  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
2072  // not available, just write what we DO have
2073  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
2074  {
2075  // general layer metadata
2076  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2077  ml->writeLayerXml( maplayerElem, *doc, context );
2078  }
2079  else if ( ! ml->originalXmlProperties().isEmpty() )
2080  {
2081  QDomDocument document;
2082  if ( document.setContent( ml->originalXmlProperties() ) )
2083  {
2084  maplayerElem = document.firstChildElement();
2085  }
2086  else
2087  {
2088  QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
2089  }
2090  }
2091 
2092  emit writeMapLayer( ml, maplayerElem, *doc );
2093 
2094  projectLayersNode.appendChild( maplayerElem );
2095  }
2096  else
2097  {
2098  // layer defined in an external project file
2099  // only save embedded layer if not managed by a legend group
2100  if ( emIt.value().second )
2101  {
2102  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2103  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
2104  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
2105  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
2106  projectLayersNode.appendChild( mapLayerElem );
2107  }
2108  }
2109  }
2110  li++;
2111  }
2112 
2113  qgisNode.appendChild( projectLayersNode );
2114 
2115  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
2116  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
2117  for ( QgsMapLayer *layer : constCustomLayerOrder )
2118  {
2119  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
2120  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
2121  layerOrderNode.appendChild( mapLayerElem );
2122  }
2123  qgisNode.appendChild( layerOrderNode );
2124 
2125  mLabelingEngineSettings->writeSettingsToProject( this );
2126 
2127  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
2128  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
2129  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
2130 
2131  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
2132  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
2133  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
2134  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
2135 
2136  // now add the optional extra properties
2137 #if 0
2138  dump_( mProperties );
2139 #endif
2140 
2141  QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 1 );
2142 
2143  if ( !mProperties.isEmpty() ) // only worry about properties if we
2144  // actually have any properties
2145  {
2146  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2147  }
2148 
2149  mMapThemeCollection->writeXml( *doc );
2150 
2151  mTransformContext.writeXml( qgisNode, context );
2152 
2153  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2154  mMetadata.writeMetadataXml( metadataElem, *doc );
2155  qgisNode.appendChild( metadataElem );
2156 
2157  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2158  qgisNode.appendChild( annotationsElem );
2159 
2160  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2161  qgisNode.appendChild( layoutElem );
2162 
2163  QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2164  qgisNode.appendChild( bookmarkElem );
2165 
2166  QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
2167  qgisNode.appendChild( viewSettingsElem );
2168 
2169  QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
2170  qgisNode.appendChild( displaySettingsElem );
2171 
2172  // now wrap it up and ship it to the project file
2173  doc->normalize(); // XXX I'm not entirely sure what this does
2174 
2175  // Create backup file
2176  if ( QFile::exists( fileName() ) )
2177  {
2178  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2179  bool ok = true;
2180  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2181  ok &= projectFile.open( QIODevice::ReadOnly );
2182 
2183  QByteArray ba;
2184  while ( ok && !projectFile.atEnd() )
2185  {
2186  ba = projectFile.read( 10240 );
2187  ok &= backupFile.write( ba ) == ba.size();
2188  }
2189 
2190  projectFile.close();
2191  backupFile.close();
2192 
2193  if ( !ok )
2194  {
2195  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2196  return false;
2197  }
2198 
2199  QFileInfo fi( fileName() );
2200  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2201  utime( backupFile.fileName().toUtf8().constData(), &tb );
2202  }
2203 
2204  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2205  {
2206  projectFile.close(); // even though we got an error, let's make
2207  // sure it's closed anyway
2208 
2209  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2210  return false;
2211  }
2212 
2213  QTemporaryFile tempFile;
2214  bool ok = tempFile.open();
2215  if ( ok )
2216  {
2217  QTextStream projectFileStream( &tempFile );
2218  doc->save( projectFileStream, 2 ); // save as utf-8
2219  ok &= projectFileStream.pos() > -1;
2220 
2221  ok &= tempFile.seek( 0 );
2222 
2223  QByteArray ba;
2224  while ( ok && !tempFile.atEnd() )
2225  {
2226  ba = tempFile.read( 10240 );
2227  ok &= projectFile.write( ba ) == ba.size();
2228  }
2229 
2230  ok &= projectFile.error() == QFile::NoError;
2231 
2232  projectFile.close();
2233  }
2234 
2235  tempFile.close();
2236 
2237  if ( !ok )
2238  {
2239  setError( tr( "Unable to save to file %1. Your project "
2240  "may be corrupted on disk. Try clearing some space on the volume and "
2241  "check file permissions before pressing save again." )
2242  .arg( projectFile.fileName() ) );
2243  return false;
2244  }
2245 
2246  setDirty( false ); // reset to pristine state
2247 
2248  emit projectSaved();
2249  return true;
2250 }
2251 
2252 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2253 {
2254  bool propertiesModified;
2255  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2256 
2257  if ( propertiesModified )
2258  setDirty( true );
2259 
2260  return success;
2261 }
2262 
2263 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2264 {
2265  bool propertiesModified;
2266  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2267 
2268  if ( propertiesModified )
2269  setDirty( true );
2270 
2271  return success;
2272 }
2273 
2274 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2275 {
2276  bool propertiesModified;
2277  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2278 
2279  if ( propertiesModified )
2280  setDirty( true );
2281 
2282  return success;
2283 }
2284 
2285 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2286 {
2287  bool propertiesModified;
2288  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2289 
2290  if ( propertiesModified )
2291  setDirty( true );
2292 
2293  return success;
2294 }
2295 
2296 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2297 {
2298  bool propertiesModified;
2299  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2300 
2301  if ( propertiesModified )
2302  setDirty( true );
2303 
2304  return success;
2305 }
2306 
2307 QStringList QgsProject::readListEntry( const QString &scope,
2308  const QString &key,
2309  const QStringList &def,
2310  bool *ok ) const
2311 {
2312  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2313 
2314  QVariant value;
2315 
2316  if ( property )
2317  {
2318  value = property->value();
2319 
2320  bool valid = QVariant::StringList == value.type();
2321  if ( ok )
2322  *ok = valid;
2323 
2324  if ( valid )
2325  {
2326  return value.toStringList();
2327  }
2328  }
2329 
2330  return def;
2331 }
2332 
2333 
2334 QString QgsProject::readEntry( const QString &scope,
2335  const QString &key,
2336  const QString &def,
2337  bool *ok ) const
2338 {
2339  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2340 
2341  QVariant value;
2342 
2343  if ( property )
2344  {
2345  value = property->value();
2346 
2347  bool valid = value.canConvert( QVariant::String );
2348  if ( ok )
2349  *ok = valid;
2350 
2351  if ( valid )
2352  return value.toString();
2353  }
2354 
2355  return def;
2356 }
2357 
2358 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2359  bool *ok ) const
2360 {
2361  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2362 
2363  QVariant value;
2364 
2365  if ( property )
2366  {
2367  value = property->value();
2368  }
2369 
2370  bool valid = value.canConvert( QVariant::Int );
2371 
2372  if ( ok )
2373  {
2374  *ok = valid;
2375  }
2376 
2377  if ( valid )
2378  {
2379  return value.toInt();
2380  }
2381 
2382  return def;
2383 }
2384 
2385 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2386  double def,
2387  bool *ok ) const
2388 {
2389  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2390  if ( property )
2391  {
2392  QVariant value = property->value();
2393 
2394  bool valid = value.canConvert( QVariant::Double );
2395  if ( ok )
2396  *ok = valid;
2397 
2398  if ( valid )
2399  return value.toDouble();
2400  }
2401 
2402  return def;
2403 }
2404 
2405 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
2406  bool *ok ) const
2407 {
2408  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2409 
2410  if ( property )
2411  {
2412  QVariant value = property->value();
2413 
2414  bool valid = value.canConvert( QVariant::Bool );
2415  if ( ok )
2416  *ok = valid;
2417 
2418  if ( valid )
2419  return value.toBool();
2420  }
2421 
2422  return def;
2423 }
2424 
2425 
2426 bool QgsProject::removeEntry( const QString &scope, const QString &key )
2427 {
2428  if ( findKey_( scope, key, mProperties ) )
2429  {
2430  removeKey_( scope, key, mProperties );
2431  setDirty( true );
2432  }
2433 
2434  return !findKey_( scope, key, mProperties );
2435 }
2436 
2437 
2438 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
2439 {
2440  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2441 
2442  QStringList entries;
2443 
2444  if ( foundProperty )
2445  {
2446  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2447 
2448  if ( propertyKey )
2449  { propertyKey->entryList( entries ); }
2450  }
2451 
2452  return entries;
2453 }
2454 
2455 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
2456 {
2457  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2458 
2459  QStringList entries;
2460 
2461  if ( foundProperty )
2462  {
2463  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2464 
2465  if ( propertyKey )
2466  { propertyKey->subkeyList( entries ); }
2467  }
2468 
2469  return entries;
2470 }
2471 
2473 {
2474  dump_( mProperties );
2475 }
2476 
2478 {
2479  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
2480  QString filePath;
2481  if ( ! absolutePaths )
2482  {
2483  // for projects stored in a custom storage, we need to ask to the
2484  // storage for the path, if the storage returns an empty path
2485  // relative paths are not supported
2486  if ( QgsProjectStorage *storage = projectStorage() )
2487  {
2488  filePath = storage->filePath( mFile.fileName() );
2489  }
2490  else
2491  {
2492  filePath = fileName();
2493  }
2494  }
2495  return QgsPathResolver( filePath );
2496 }
2497 
2498 QString QgsProject::readPath( const QString &src ) const
2499 {
2500  return pathResolver().readPath( src );
2501 }
2502 
2503 QString QgsProject::writePath( const QString &src ) const
2504 {
2505  return pathResolver().writePath( src );
2506 }
2507 
2508 void QgsProject::setError( const QString &errorMessage )
2509 {
2510  mErrorMessage = errorMessage;
2511 }
2512 
2513 QString QgsProject::error() const
2514 {
2515  return mErrorMessage;
2516 }
2517 
2518 void QgsProject::clearError()
2519 {
2520  setError( QString() );
2521 }
2522 
2524 {
2525  delete mBadLayerHandler;
2526  mBadLayerHandler = handler;
2527 }
2528 
2529 QString QgsProject::layerIsEmbedded( const QString &id ) const
2530 {
2531  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
2532  if ( it == mEmbeddedLayers.constEnd() )
2533  {
2534  return QString();
2535  }
2536  return it.value().first;
2537 }
2538 
2539 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
2540  bool saveFlag, QgsProject::ReadFlags flags )
2541 {
2542  QgsDebugCall;
2543 
2544  static QString sPrevProjectFilePath;
2545  static QDateTime sPrevProjectFileTimestamp;
2546  static QDomDocument sProjectDocument;
2547 
2548  QString qgsProjectFile = projectFilePath;
2549  QgsProjectArchive archive;
2550  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
2551  {
2552  archive.unzip( projectFilePath );
2553  qgsProjectFile = archive.projectFile();
2554  }
2555 
2556  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
2557 
2558  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
2559  {
2560  sPrevProjectFilePath.clear();
2561 
2562  QFile projectFile( qgsProjectFile );
2563  if ( !projectFile.open( QIODevice::ReadOnly ) )
2564  {
2565  return false;
2566  }
2567 
2568  if ( !sProjectDocument.setContent( &projectFile ) )
2569  {
2570  return false;
2571  }
2572 
2573  sPrevProjectFilePath = projectFilePath;
2574  sPrevProjectFileTimestamp = projectFileTimestamp;
2575  }
2576 
2577  // does project store paths absolute or relative?
2578  bool useAbsolutePaths = true;
2579 
2580  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
2581  if ( !propertiesElem.isNull() )
2582  {
2583  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
2584  if ( !absElem.isNull() )
2585  {
2586  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
2587  }
2588  }
2589 
2590  QgsReadWriteContext embeddedContext;
2591  if ( !useAbsolutePaths )
2592  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
2593  embeddedContext.setProjectTranslator( this );
2594  embeddedContext.setTransformContext( transformContext() );
2595 
2596  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
2597  if ( projectLayersElem.isNull() )
2598  {
2599  return false;
2600  }
2601 
2602  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
2603  for ( int i = 0; i < mapLayerNodes.size(); ++i )
2604  {
2605  // get layer id
2606  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
2607  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
2608  if ( id == layerId )
2609  {
2610  // layer can be embedded only once
2611  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
2612  {
2613  return false;
2614  }
2615 
2616  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
2617 
2618  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
2619  {
2620  return true;
2621  }
2622  else
2623  {
2624  mEmbeddedLayers.remove( layerId );
2625  return false;
2626  }
2627  }
2628  }
2629 
2630  return false;
2631 }
2632 
2633 
2634 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags )
2635 {
2636  QString qgsProjectFile = projectFilePath;
2637  QgsProjectArchive archive;
2638  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
2639  {
2640  archive.unzip( projectFilePath );
2641  qgsProjectFile = archive.projectFile();
2642  }
2643 
2644  // open project file, get layer ids in group, add the layers
2645  QFile projectFile( qgsProjectFile );
2646  if ( !projectFile.open( QIODevice::ReadOnly ) )
2647  {
2648  return nullptr;
2649  }
2650 
2651  QDomDocument projectDocument;
2652  if ( !projectDocument.setContent( &projectFile ) )
2653  {
2654  return nullptr;
2655  }
2656 
2657  QgsReadWriteContext context;
2658  context.setPathResolver( pathResolver() );
2659  context.setProjectTranslator( this );
2661 
2663 
2664  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2665  if ( !layerTreeElem.isNull() )
2666  {
2667  root->readChildrenFromXml( layerTreeElem, context );
2668  }
2669  else
2670  {
2671  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2672  }
2673 
2674  QgsLayerTreeGroup *group = root->findGroup( groupName );
2675  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
2676  {
2677  // embedded groups cannot be embedded again
2678  delete root;
2679  return nullptr;
2680  }
2681 
2682  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
2683  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
2684  delete root;
2685  root = nullptr;
2686 
2687  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2688  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2689 
2690  // set "embedded" to all children + load embedded layers
2691  mLayerTreeRegistryBridge->setEnabled( false );
2692  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
2693  mLayerTreeRegistryBridge->setEnabled( true );
2694 
2695  // consider the layers might be identify disabled in its project
2696  const auto constFindLayerIds = newGroup->findLayerIds();
2697  for ( const QString &layerId : constFindLayerIds )
2698  {
2699  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2700  if ( layer )
2701  {
2702  layer->resolveReferences( this );
2703  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
2704  }
2705  }
2706 
2707  return newGroup;
2708 }
2709 
2710 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
2711 {
2712  const auto constChildren = group->children();
2713  for ( QgsLayerTreeNode *child : constChildren )
2714  {
2715  // all nodes in the subtree will have "embedded" custom property set
2716  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2717 
2718  if ( QgsLayerTree::isGroup( child ) )
2719  {
2720  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
2721  }
2722  else if ( QgsLayerTree::isLayer( child ) )
2723  {
2724  // load the layer into our project
2725  QList<QDomNode> brokenNodes;
2726  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
2727  }
2728  }
2729 }
2730 
2732 {
2733  return mEvaluateDefaultValues;
2734 }
2735 
2737 {
2738  if ( evaluateDefaultValues == mEvaluateDefaultValues )
2739  return;
2740 
2741  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2742  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
2743  for ( ; layerIt != layers.constEnd(); ++layerIt )
2744  {
2745  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
2746  if ( vl )
2747  {
2749  }
2750  }
2751 
2752  mEvaluateDefaultValues = evaluateDefaultValues;
2753 }
2754 
2756 {
2757  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2759 }
2760 
2762 {
2763  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2764 }
2765 
2767 {
2768  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2769  if ( !distanceUnitString.isEmpty() )
2770  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2771 
2772  //fallback to QGIS default measurement unit
2773  QgsSettings s;
2774  bool ok = false;
2775  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2776  return ok ? type : QgsUnitTypes::DistanceMeters;
2777 }
2778 
2780 {
2781  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2782 }
2783 
2785 {
2786  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2787  if ( !areaUnitString.isEmpty() )
2788  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2789 
2790  //fallback to QGIS default area unit
2791  QgsSettings s;
2792  bool ok = false;
2793  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2794  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2795 }
2796 
2798 {
2799  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2800 }
2801 
2802 QString QgsProject::homePath() const
2803 {
2804  if ( !mCachedHomePath.isEmpty() )
2805  return mCachedHomePath;
2806 
2807  QFileInfo pfi( fileName() );
2808 
2809  if ( !mHomePath.isEmpty() )
2810  {
2811  QFileInfo homeInfo( mHomePath );
2812  if ( !homeInfo.isRelative() )
2813  {
2814  mCachedHomePath = mHomePath;
2815  return mHomePath;
2816  }
2817  }
2818  else if ( !fileName().isEmpty() )
2819  {
2820  mCachedHomePath = pfi.path();
2821 
2822  return mCachedHomePath;
2823  }
2824 
2825  if ( !pfi.exists() )
2826  {
2827  mCachedHomePath = mHomePath;
2828  return mHomePath;
2829  }
2830 
2831  if ( !mHomePath.isEmpty() )
2832  {
2833  // path is relative to project file
2834  mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
2835  }
2836  else
2837  {
2838  mCachedHomePath = pfi.canonicalPath();
2839  }
2840  return mCachedHomePath;
2841 }
2842 
2844 {
2845  return mHomePath;
2846 }
2847 
2849 {
2850  return mRelationManager;
2851 }
2852 
2854 {
2855  return mLayoutManager.get();
2856 }
2857 
2859 {
2860  return mLayoutManager.get();
2861 }
2862 
2864 {
2865  return mBookmarkManager;
2866 }
2867 
2869 {
2870  return mBookmarkManager;
2871 }
2872 
2874 {
2875  return mViewSettings;
2876 }
2877 
2879 {
2880  return mViewSettings;
2881 }
2882 
2884 {
2885  return mDisplaySettings;
2886 }
2887 
2889 {
2890  return mDisplaySettings;
2891 }
2892 
2894 {
2895  return mRootGroup;
2896 }
2897 
2899 {
2900  return mMapThemeCollection.get();
2901 }
2902 
2904 {
2905  return mAnnotationManager.get();
2906 }
2907 
2909 {
2910  return mAnnotationManager.get();
2911 }
2912 
2913 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2914 {
2915  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
2916  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
2917  {
2918  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2919  continue;
2920 
2921  if ( layers.contains( it.value() ) )
2922  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
2923  else
2924  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
2925  }
2926 
2930 }
2931 
2932 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2933 {
2934  QList<QgsMapLayer *> nonIdentifiableLayers;
2935  nonIdentifiableLayers.reserve( layerIds.count() );
2936  for ( const QString &layerId : layerIds )
2937  {
2938  QgsMapLayer *layer = mapLayer( layerId );
2939  if ( layer )
2940  nonIdentifiableLayers << layer;
2941  }
2943  setNonIdentifiableLayers( nonIdentifiableLayers );
2945 }
2946 
2947 QStringList QgsProject::nonIdentifiableLayers() const
2948 {
2949  QStringList nonIdentifiableLayers;
2950 
2951  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2952  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
2953  {
2954  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2955  {
2956  nonIdentifiableLayers.append( it.value()->id() );
2957  }
2958  }
2959  return nonIdentifiableLayers;
2960 }
2961 
2963 {
2964  return mAutoTransaction;
2965 }
2966 
2968 {
2969  if ( autoTransaction != mAutoTransaction )
2970  {
2971  mAutoTransaction = autoTransaction;
2972 
2973  if ( autoTransaction )
2974  onMapLayersAdded( mapLayers().values() );
2975  else
2976  cleanTransactionGroups( true );
2977  }
2978 }
2979 
2980 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2981 {
2982  return mTransactionGroups;
2983 }
2984 
2985 
2986 //
2987 // QgsMapLayerStore methods
2988 //
2989 
2990 
2992 {
2993  return mLayerStore->count();
2994 }
2995 
2997 {
2998  return mLayerStore->validCount();
2999 }
3000 
3001 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
3002 {
3003  return mLayerStore->mapLayer( layerId );
3004 }
3005 
3006 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
3007 {
3008  return mLayerStore->mapLayersByName( layerName );
3009 }
3010 
3011 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
3012 {
3013  QList<QgsMapLayer *> layers;
3014  const auto constMapLayers { mLayerStore->mapLayers() };
3015  for ( const auto &l : constMapLayers )
3016  {
3017  if ( ! l->shortName().isEmpty() )
3018  {
3019  if ( l->shortName() == shortName )
3020  layers << l;
3021  }
3022  else if ( l->name() == shortName )
3023  {
3024  layers << l;
3025  }
3026  }
3027  return layers;
3028 }
3029 
3030 bool QgsProject::unzip( const QString &filename, QgsProject::ReadFlags flags )
3031 {
3032  clearError();
3033  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3034 
3035  // unzip the archive
3036  if ( !archive->unzip( filename ) )
3037  {
3038  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
3039  return false;
3040  }
3041 
3042  // test if zip provides a .qgs file
3043  if ( archive->projectFile().isEmpty() )
3044  {
3045  setError( tr( "Zip archive does not provide a project file" ) );
3046  return false;
3047  }
3048 
3049  // load auxiliary storage
3050  if ( !archive->auxiliaryStorageFile().isEmpty() )
3051  {
3052  // database file is already a copy as it's been unzipped. So we don't open
3053  // auxiliary storage in copy mode in this case
3054  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
3055  }
3056  else
3057  {
3058  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
3059  }
3060 
3061  // read the project file
3062  if ( ! readProjectFile( archive->projectFile(), flags ) )
3063  {
3064  setError( tr( "Cannot read unzipped qgs project file" ) );
3065  return false;
3066  }
3067 
3068  // keep the archive and remove the temporary .qgs file
3069  mArchive = std::move( archive );
3070  mArchive->clearProjectFile();
3071 
3072  return true;
3073 }
3074 
3075 bool QgsProject::zip( const QString &filename )
3076 {
3077  clearError();
3078 
3079  // save the current project in a temporary .qgs file
3080  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3081  const QString baseName = QFileInfo( filename ).baseName();
3082  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
3083  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
3084 
3085  bool writeOk = false;
3086  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3087  {
3088  writeOk = writeProjectFile( qgsFile.fileName() );
3089  qgsFile.close();
3090  }
3091 
3092  // stop here with an error message
3093  if ( ! writeOk )
3094  {
3095  setError( tr( "Unable to write temporary qgs file" ) );
3096  return false;
3097  }
3098 
3099  // save auxiliary storage
3100  const QFileInfo info( qgsFile );
3101  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
3102 
3103  if ( ! saveAuxiliaryStorage( asFileName ) )
3104  {
3105  const QString err = mAuxiliaryStorage->errorString();
3106  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
3107  return false;
3108  }
3109 
3110  // create the archive
3111  archive->addFile( qgsFile.fileName() );
3112  archive->addFile( asFileName );
3113 
3114  // zip
3115  if ( !archive->zip( filename ) )
3116  {
3117  setError( tr( "Unable to perform zip" ) );
3118  return false;
3119  }
3120 
3121  return true;
3122 }
3123 
3125 {
3126  return QgsZipUtils::isZipFile( mFile.fileName() );
3127 }
3128 
3129 QList<QgsMapLayer *> QgsProject::addMapLayers(
3130  const QList<QgsMapLayer *> &layers,
3131  bool addToLegend,
3132  bool takeOwnership )
3133 {
3134  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
3135  if ( !myResultList.isEmpty() )
3136  {
3137  // Update transform context
3138  for ( auto &l : myResultList )
3139  {
3140  l->setTransformContext( transformContext() );
3141  }
3142  if ( addToLegend )
3143  {
3144  emit legendLayersAdded( myResultList );
3145  }
3146  }
3147 
3148  if ( mAuxiliaryStorage )
3149  {
3150  for ( QgsMapLayer *mlayer : myResultList )
3151  {
3152  if ( mlayer->type() != QgsMapLayerType::VectorLayer )
3153  continue;
3154 
3155  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
3156  if ( vl )
3157  {
3158  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
3159  }
3160  }
3161  }
3162 
3163  return myResultList;
3164 }
3165 
3166 QgsMapLayer *
3168  bool addToLegend,
3169  bool takeOwnership )
3170 {
3171  QList<QgsMapLayer *> addedLayers;
3172  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
3173  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
3174 }
3175 
3176 void QgsProject::removeMapLayers( const QStringList &layerIds )
3177 {
3178  mLayerStore->removeMapLayers( layerIds );
3179 }
3180 
3181 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3182 {
3183  mLayerStore->removeMapLayers( layers );
3184 }
3185 
3186 void QgsProject::removeMapLayer( const QString &layerId )
3187 {
3188  mLayerStore->removeMapLayer( layerId );
3189 }
3190 
3192 {
3193  mLayerStore->removeMapLayer( layer );
3194 }
3195 
3197 {
3198  return mLayerStore->takeMapLayer( layer );
3199 }
3200 
3202 {
3203  mLayerStore->removeAllMapLayers();
3204 }
3205 
3207 {
3208  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3209  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3210  for ( ; it != layers.constEnd(); ++it )
3211  {
3212  it.value()->reload();
3213  }
3214 }
3215 
3216 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3217 {
3218  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3219 }
3220 
3221 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3222 {
3223  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3224 }
3225 
3227 {
3228  QgsSettings settings;
3229  QgsCoordinateReferenceSystem defaultCrs;
3230 
3231  // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
3232  // in the meantime, we have a slightly hacky way to read the settings key using an enum which isn't available (since it lives in app)
3233  if ( settings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
3234  || settings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == 2 )
3235  {
3236  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
3237  defaultCrs = crs();
3238  }
3239  else
3240  {
3241  // global crs
3242  QString layerDefaultCrs = settings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
3243  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
3244  }
3245 
3246  return defaultCrs;
3247 }
3248 
3250 {
3251  mTrustLayerMetadata = trust;
3252 
3253  auto layers = mapLayers();
3254  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3255  {
3256  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3257  if ( vl )
3258  {
3259  vl->setReadExtentFromXml( trust );
3260  }
3261  }
3262 }
3263 
3264 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
3265 {
3266  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3267  bool empty = true;
3268  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3269  {
3270  if ( it.value()->type() != QgsMapLayerType::VectorLayer )
3271  continue;
3272 
3273  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3274  if ( vl && vl->auxiliaryLayer() )
3275  {
3276  vl->auxiliaryLayer()->save();
3277  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
3278  }
3279  }
3280 
3281  if ( !mAuxiliaryStorage->exists( *this ) && filename.isEmpty() && empty )
3282  {
3283  return true; // it's not an error
3284  }
3285  else if ( !filename.isEmpty() )
3286  {
3287  return mAuxiliaryStorage->saveAs( filename );
3288  }
3289  else
3290  {
3291  return mAuxiliaryStorage->saveAs( *this );
3292  }
3293 }
3294 
3296 {
3297  return mAuxiliaryStorage.get();
3298 }
3299 
3301 {
3302  return mAuxiliaryStorage.get();
3303 }
3304 
3306 {
3307  return mMetadata;
3308 }
3309 
3311 {
3312  if ( metadata == mMetadata )
3313  return;
3314 
3315  mMetadata = metadata;
3316  mProjectScope.reset();
3317 
3318  emit metadataChanged();
3319 
3320  setDirty( true );
3321 }
3322 
3323 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
3324 {
3325  QSet<QgsMapLayer *> requiredLayers;
3326 
3327  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3328  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3329  {
3330  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3331  {
3332  requiredLayers.insert( it.value() );
3333  }
3334  }
3335  return requiredLayers;
3336 }
3337 
3338 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
3339 {
3340  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3341  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3342  {
3343  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3344  continue;
3345 
3346  if ( layers.contains( it.value() ) )
3347  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
3348  else
3349  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
3350  }
3351 }
3352 
3354 {
3355  // save colors to project
3356  QStringList customColors;
3357  QStringList customColorLabels;
3358 
3359  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
3360  for ( ; colorIt != colors.constEnd(); ++colorIt )
3361  {
3362  QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
3363  QString label = ( *colorIt ).second;
3364  customColors.append( color );
3365  customColorLabels.append( label );
3366  }
3367  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
3368  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
3369  mProjectScope.reset();
3370  emit projectColorsChanged();
3371 }
3372 
3373 void QgsProject::setBackgroundColor( const QColor &color )
3374 {
3375  if ( mBackgroundColor == color )
3376  return;
3377 
3378  mBackgroundColor = color;
3379  emit backgroundColorChanged();
3380 }
3381 
3382 QColor QgsProject::backgroundColor() const
3383 {
3384  return mBackgroundColor;
3385 }
3386 
3387 void QgsProject::setSelectionColor( const QColor &color )
3388 {
3389  if ( mSelectionColor == color )
3390  return;
3391 
3392  mSelectionColor = color;
3393  emit selectionColorChanged();
3394 }
3395 
3396 QColor QgsProject::selectionColor() const
3397 {
3398  return mSelectionColor;
3399 }
3400 
3401 void QgsProject::setMapScales( const QVector<double> &scales )
3402 {
3403  mViewSettings->setMapScales( scales );
3404 }
3405 
3406 QVector<double> QgsProject::mapScales() const
3407 {
3408  return mViewSettings->mapScales();
3409 }
3410 
3412 {
3413  mViewSettings->setUseProjectScales( enabled );
3414 }
3415 
3417 {
3418  return mViewSettings->useProjectScales();
3419 }
3420 
3421 void QgsProject::generateTsFile( const QString &locale )
3422 {
3423  QgsTranslationContext translationContext;
3424  translationContext.setProject( this );
3425  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
3426 
3427  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
3428 
3429  translationContext.writeTsFile( locale );
3430 }
3431 
3432 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
3433 {
3434  if ( !mTranslator )
3435  {
3436  return sourceText;
3437  }
3438 
3439  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
3440 
3441  if ( result.isEmpty() )
3442  {
3443  return sourceText;
3444  }
3445  return result;
3446 }
3447 
3449 {
3450  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
3451  if ( !layers.empty() )
3452  {
3453  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3454  {
3455  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
3456  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3457  {
3458  if ( !( ( *it )->accept( visitor ) ) )
3459  return false;
3460 
3461  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3462  return false;
3463  }
3464  }
3465  }
3466 
3467  if ( !mLayoutManager->accept( visitor ) )
3468  return false;
3469 
3470  if ( !mAnnotationManager->accept( visitor ) )
3471  return false;
3472 
3473  return true;
3474 }
3475 
3477 GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
3478  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3479 {
3480  if ( !project )
3481  return;
3482 
3483  //build up color list from project. Do this in advance for speed
3484  QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
3485  QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
3486 
3487  //generate list from custom colors
3488  int colorIndex = 0;
3489  for ( QStringList::iterator it = colorStrings.begin();
3490  it != colorStrings.end(); ++it )
3491  {
3492  QColor color = QgsSymbolLayerUtils::decodeColor( *it );
3493  QString label;
3494  if ( colorLabels.length() > colorIndex )
3495  {
3496  label = colorLabels.at( colorIndex );
3497  }
3498 
3499  mColors.insert( label.toLower(), color );
3500  colorIndex++;
3501  }
3502 }
3503 
3504 GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
3505  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3506  , mColors( colors )
3507 {
3508 }
3509 
3510 QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3511 {
3512  QString colorName = values.at( 0 ).toString().toLower();
3513  if ( mColors.contains( colorName ) )
3514  {
3515  return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
3516  }
3517  else
3518  return QVariant();
3519 }
3520 
3521 QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
3522 {
3523  return new GetNamedProjectColor( mColors );
3524 }
Q_DECL_DEPRECATED QStringList nonIdentifiableLayers() const
Gets the list of layers which currently should not be taken into account on map identification.
Class for parsing and evaluation of expressions (formerly called "search strings").
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:491
void setTrustLayerMetadata(bool trust)
Sets the trust option allowing to indicate if the extent has to be read from the XML document when da...
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 error() const
Returns error message from previous read/write.
Single variable definition for use within a QgsExpressionContextScope.
QString name
Name of the project - equivalent to a file&#39;s base name (i.e. without path and extension).
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:486
static Q_INVOKABLE QgsUnitTypes::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
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer *> &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Base class for all map layer types.
Definition: qgsmaplayer.h:79
void layersAdded(const QList< QgsMapLayer *> &layers)
Emitted when one or more layers were added to the registry.
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Definition: qgsarchive.cpp:139
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:623
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings&#39;s state from a DOM element.
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.
QgsRelationManager * relationManager() const
const QgsAuxiliaryStorage * auxiliaryStorage() const
Returns the current const auxiliary storage.
This is an abstract base class for any elements of a drag and drop form.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource...
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
void selectionColorChanged()
Emitted whenever the project&#39;s selection color has been changed.
QgsMapLayerType type() const
Returns the type of the layer.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer *> &layers)
A list of layers with which intersections should be avoided.
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. ...
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
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:816
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
const QgsProjectMetadata & metadata() const
Returns a reference to the project&#39;s metadata store.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
const QgsProjectDisplaySettings * displaySettings() const
Returns the project&#39;s display settings, which settings and properties relating to how a QgsProject sh...
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ...
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
void backgroundColorChanged()
Emitted whenever the project&#39;s canvas background color has been changed.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
Definition: qgsproject.cpp:956
QString toProj() const
Returns a Proj string representation of this CRS.
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.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
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:122
Class used to work with layer dependencies stored in a XML project or layer definition file...
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
QString providerType() const
Returns the provider type (provider key) for this layer.
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...
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:652
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:731
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:588
Class providing some utility methods to manage auxiliary storage.
void crsChanged()
Emitted when the CRS of the project has changed.
void layersAdded(const QList< QgsMapLayer *> &layers)
Emitted when one or more layers were added to the store.
Q_DECL_DEPRECATED void mapScalesChanged()
Emitted when the list of custom project map scales changes.
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.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
void configChanged()
Emitted whenever the configuration is changed.
Container of fields for a vector layer.
Definition: qgsfields.h:42
void labelingEngineSettingsChanged()
Emitted when global configuration of the labeling engine changes.
void projectColorsChanged()
Emitted whenever the project&#39;s color scheme has been changed.
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.
int count() const
Returns the number of registered layers.
void metadataChanged()
Emitted when the project&#39;s metadata is changed.
Manages storage of a set of bookmarks.
QDateTime lastModified
Date and local time when the file was last modified.
bool useProjectScales() const
Returns true if project mapScales() are enabled.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
void updateRelationsStatus()
Updates relations status.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
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 legendLayersAdded(const QList< QgsMapLayer *> &layers)
Emitted, when a layer was added to the registry and the legend.
bool isValid() const
Returns the status of the layer.
An interface for classes which can visit style entity (e.g.
void removeKey(const QString &keyName)
Removes the specified key.
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.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
void reset()
reset to default values
void allLayersRemoved()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results...
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
QgsMapLayerStore * layerStore()
Returns a pointer to the project&#39;s internal layer store.
const QgsBookmarkManager * bookmarkManager() const
Returns the project&#39;s bookmark manager, which manages bookmarks within the project.
QgsCoordinateTransformContext transformContext() const
Returns a copy of the project&#39;s coordinate transform context, which stores various information regard...
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
void clear()
Remove any relation managed by this class.
QgsProject(QObject *parent=nullptr)
Create a new QgsProject.
Definition: qgsproject.cpp:357
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.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
QgsProjectVersion getVersion(const QDomDocument &doc)
Returns the version string found in the given DOM document.
Definition: qgsproject.cpp:933
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
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.
static QString encodeColor(const QColor &color)
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:115
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.
QgsProjectProperty * find(const QString &propertyName) const
Attempts to find a property with a matching sub-key name.
Q_DECL_DEPRECATED void setNonIdentifiableLayers(const QList< QgsMapLayer *> &layers)
Set a list of layers which should not be taken into account on map identification.
void setAuthor(const QString &author)
Sets the project author string.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
QString presetHomePath() const
Returns any manual project home path setting, or an empty string if not set.
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.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
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...
static QString version()
Version string.
Definition: qgis.cpp:276
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
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...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QString name() const
The name of the property is used as identifier.
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.
QColor selectionColor() const
Returns the color used to highlight selected features.
QString projectFile() const
Returns the current .qgs project file or an empty string if there&#39;s none.
Definition: qgsarchive.cpp:126
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager&#39;s state from a DOM element, restoring all bookmarks present in the XML document...
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.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
Don&#39;t load print layouts. Improves project read time if layouts are not required, and allows projects...
void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets data coordinate transform context to transformContext.
const QString & typeName
A class to describe the version of a project.
QString saveUser() const
Returns the user name that did the last save.
Definition: qgsproject.cpp:476
QString layerIsEmbedded(const QString &id) const
Returns project file path if layer is embedded from other project file. Returns empty string if layer...
int count() const
Returns the number of sub-keys contained by this property.
virtual void clearKeys()
Deletes any sub-nodes from the property.
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the store.
QVector< double > mapScales() const
Returns the list of custom project map scales.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const
Returns the default CRS for new layers based on the settings and the current project CRS...
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
void dumpProperties() const
Dump out current project properties to stderr.
void reset()
Resets the settings to a default state.
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).
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
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.
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...
QStringList subkeyList(const QString &scope, const QString &key) const
Returns keys with keys – do not return keys that contain only values.
void clear()
Clear any information from this layer tree.
Don&#39;t resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:542
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
void setCrs(const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid=false)
Sets the project&#39;s native coordinate reference system.
Definition: qgsproject.cpp:678
QList< QgsLayerTreeGroup * > findGroups() const
Find all group layer nodes.
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 setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
Contains information relating to a node (i.e.
void dump(int tabs=0) const override
Dumps out the keys and values.
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
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.
void dataSourceChanged()
Emitted whenever the layer&#39;s data source has been changed.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:91
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setEllipsoid(const QString &ellipsoid)
Sets the project&#39;s ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:701
~QgsProject() override
Definition: qgsproject.cpp:429
void setProjectColors(const QgsNamedColorList &colors)
Sets the colors for the project&#39;s color scheme (see QgsProjectColorScheme).
void insertChildNodes(int index, const QList< QgsLayerTreeNode *> &nodes)
Insert existing nodes at specified position.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, bool saveFlag=true, QgsProject::ReadFlags flags=nullptr)
Creates a maplayer instance defined in an arbitrary project file.
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.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:49
void removeAllMapLayers()
Removes all registered layers.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void clear()
Removes and deletes all bookmarks from the manager.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file...
An expression node for expression functions.
virtual bool isValue() const =0
Returns true if the property is a QgsProjectPropertyValue.
Contains information about the context in which a coordinate transform is executed.
const QgsProjectViewSettings * viewSettings() const
Returns the project&#39;s view settings, which contains settings and properties relating to how a QgsProj...
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Remove a given key.
Definition: qgsproject.cpp:297
QgsEditFormConfig editFormConfig
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.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
void setProviderProperty(ProviderProperty property, const QVariant &value)
Allows setting arbitrary properties on the provider.
QgsProjectStorage * projectStorage() const
Returns pointer to project storage implementation that handles read/write of the project file...
Definition: qgsproject.cpp:618
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...
Q_DECL_DEPRECATED void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
QgsAnnotationManager * annotationManager()
Returns pointer to the project&#39;s annotation manager.
#define QgsDebugCall
Definition: qgslogger.h:37
static QString userLoginName()
Returns the user&#39;s operating system login account name.
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
const QgsLayoutManager * layoutManager() const
Returns the project&#39;s layout manager, which manages print layouts, atlases and reports within the pro...
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:91
void setSelectionColor(const QColor &color)
Sets the color used to highlight selected features.
bool loadAuxiliaryLayer(const QgsAuxiliaryStorage &storage, const QString &key=QString())
Loads the auxiliary layer for this vector layer.
bool removeLayers(const QList< QgsMapLayer *> &layers)
Removes the specified layers from the individual layer configuration.
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
Definition: qgsproject.cpp:535
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a 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:637
void setMetadata(const QgsProjectMetadata &metadata)
Sets the project&#39;s metadata store.
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
bool topologicalEditing() const
Convenience function to query topological editing status.
bool readLayer(const QDomNode &layerNode)
Reads the layer described in the associated DOM node.
QString homePath() const
Returns the project&#39;s home path.
bool isEmpty() const
Returns true if this property contains no sub-keys.
QgsMapThemeCollection * mapThemeCollection()
Returns pointer to the project&#39;s map theme collection.
Contains settings and properties relating to how a QgsProject should be displayed inside map canvas...
QString fileName() const
Returns the project&#39;s file name.
Project property key node.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:66
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project&#39;s layer tree.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context&#39;s state to a DOM element.
Q_DECL_DEPRECATED bool useProjectScales() const
Returns true if project mapScales() are enabled.
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed...
void setPresetHomePath(const QString &path)
Sets the project&#39;s home path.
Definition: qgsproject.cpp:503
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:732
void setFileName(const QString &fileName)
Sets the fileName of the TS file.
void setBackgroundColor(const QColor &color)
Sets the default background color used by default map canvases.
void registerTranslation(const QString &context, const QString &source)
Registers the source to be translated.
void writeProject(QDomDocument &)
Emitted when the project is being written.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:885
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
void oldProjectVersionWarning(const QString &)
Emitted when an old project file is read.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings&#39;s state from a DOM element.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project&#39;s associated file.
Definition: qgsproject.cpp:613
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...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
QString source() const
Returns the source for the layer.
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
void setProject(QgsProject *project)
Sets the project being translated.
void registerTranslatableContainers(QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId)
Registers the containers that require translation into the translationContext.
Definition: qgsproject.cpp:517
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed...
Q_DECL_DEPRECATED QVector< double > mapScales() const
Returns the list of custom project map scales.
This class manages a set of relations between layers.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QgsSnappingConfig snappingConfig() const
The snapping configuration for this project.
Stores global configuration for labeling engine.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
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
This class represents a coordinate reference system (CRS).
Q_DECL_DEPRECATED void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
static void storeOriginalLayersProperties(QgsLayerTreeGroup *group, const QDomDocument *doc)
Stores in a layer&#39;s originalXmlProperties the layer properties information.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project&#39;s coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:716
bool read(const QString &filename, QgsProject::ReadFlags flags=nullptr)
Reads given project file from the given file.
void setTitle(const QString &title)
Sets the project&#39;s title.
Definition: qgsproject.cpp:459
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.
QgsCoordinateReferenceSystem crs() const
Returns the project&#39;s native coordinate reference system.
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
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:138
void loadingLayerMessageReceived(const QString &layerName, const QList< QgsReadWriteContext::ReadWriteMessage > &messages)
Emitted when loading layers has produced some messages.
void mapScalesChanged()
Emitted when the list of custom project map scales changes.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
An Abstract Base Class for QGIS project property hierarchys.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
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.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
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.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
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 avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
Contains settings and properties relating to how a QgsProject should display values such as map coord...
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.
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:471
This is a container for configuration of the snapping of the project.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QgsUnitTypes::DistanceUnit distanceUnits() const
Convenience function to query default distance measurement units for project.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
QVariantMap customVariables() const
A map of custom project variables.
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted after the basic initialization of a layer from the project file is done.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Don&#39;t resolve layer paths (i.e. don&#39;t load any layer content). Dramatically improves project read tim...
QString ellipsoid() const
Returns a proj string representing the project&#39;s ellipsoid setting, e.g., "WGS84".
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QString baseName() const
Returns the base name of the project file without the path and without extension - derived from fileN...
Definition: qgsproject.cpp:659
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
Resolves relative paths into absolute paths and vice versa.
bool addLayers(const QList< QgsMapLayer *> &layers)
Adds the specified layers as individual layers to the configuration with standard configuration...
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 RDBMS data source URI (e.g.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
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:648
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:133
void setEvaluateDefaultValues(bool evaluateDefaultValues)
Defines if default values should be evaluated on provider side when requested and not when committed...
Represents a vector layer which manages a vector based data sets.
QgsMapLayer * mapLayer(const QString &id) const
Retrieve a pointer to a layer by layer id.
QColor backgroundColor() const
Returns the default background color used by default map canvases.
void reset()
Resets the settings to a default state.
QString name() const
Returns the name of this element.
QList< QgsVectorLayer * > avoidIntersectionsLayers() const
A list of layers with which intersections should be avoided.
QgsProjectStorage * projectStorageFromUri(const QString &uri)
Returns storage implementation if the URI matches one. Returns nullptr otherwise (it is a normal file...
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:646
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
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:210
AreaUnit
Units of area.
Definition: qgsunittypes.h:92
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:137
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QStringList entryList(const QString &scope, const QString &key) const
Returns keys with values – do not return keys that contain other keys.
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:853
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
Expression function for use within a QgsExpressionContextScope.
QString authid() const
Returns the authority identifier for the CRS.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets project&#39;s global labeling engine settings.
void clear()
Clears the project, removing all settings and resetting it back to an empty, default state...
Definition: qgsproject.cpp:731
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...
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.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
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.
static QColor decodeColor(const QString &str)
QString saveUserFullName() const
Returns the full user name that did the last save.
Definition: qgsproject.cpp:481
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.
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.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project&#39;s global labeling engine settings.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
int validCount() const
Returns the number of registered valid layers.
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags=nullptr)
Create layer group instance defined in an arbitrary project file.