QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
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 "qgsmaplayerfactory.h"
28 #include "qgspluginlayer.h"
29 #include "qgspluginlayerregistry.h"
31 #include "qgssnappingconfig.h"
32 #include "qgspathresolver.h"
33 #include "qgsprojectstorage.h"
35 #include "qgsprojectversion.h"
36 #include "qgsrasterlayer.h"
37 #include "qgsreadwritecontext.h"
38 #include "qgsrelationmanager.h"
39 #include "qgsannotationmanager.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 "qgsmeshlayer.h"
49 #include "qgslayoutmanager.h"
50 #include "qgsbookmarkmanager.h"
51 #include "qgsmaplayerstore.h"
52 #include "qgsziputils.h"
53 #include "qgsauxiliarystorage.h"
54 #include "qgscolorutils.h"
55 #include "qgsapplication.h"
57 #include "qgsstyleentityvisitor.h"
58 #include "qgsprojectviewsettings.h"
60 #include "qgsprojecttimesettings.h"
61 #include "qgsvectortilelayer.h"
62 #include "qgstiledscenelayer.h"
63 #include "qgsruntimeprofiler.h"
64 #include "qgsannotationlayer.h"
65 #include "qgspointcloudlayer.h"
67 #include "qgsgrouplayer.h"
68 #include "qgsmapviewsmanager.h"
70 #include "qgscombinedstylemodel.h"
71 #include "qgsprojectgpssettings.h"
72 #include "qgsthreadingutils.h"
73 #include "qgssensormanager.h"
74 #include "qgsproviderregistry.h"
77 
78 #include <algorithm>
79 #include <QApplication>
80 #include <QFileInfo>
81 #include <QDomNode>
82 #include <QObject>
83 #include <QTextStream>
84 #include <QTemporaryFile>
85 #include <QDir>
86 #include <QUrl>
87 #include <QStandardPaths>
88 #include <QUuid>
89 #include <QRegularExpression>
90 #include <QThreadPool>
91 
92 #ifdef _MSC_VER
93 #include <sys/utime.h>
94 #else
95 #include <utime.h>
96 #endif
97 
98 // canonical project instance
99 QgsProject *QgsProject::sProject = nullptr;
100 
109 QStringList makeKeyTokens_( const QString &scope, const QString &key )
110 {
111  QStringList keyTokens = QStringList( scope );
112  keyTokens += key.split( '/', Qt::SkipEmptyParts );
113 
114  // be sure to include the canonical root node
115  keyTokens.push_front( QStringLiteral( "properties" ) );
116 
117  //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.
118  for ( int i = 0; i < keyTokens.size(); ++i )
119  {
120  const QString keyToken = keyTokens.at( i );
121 
122  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
123  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
124  const thread_local QRegularExpression sInvalidRegexp = QRegularExpression( QStringLiteral( "([^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\-\\.0-9\\x{B7}\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]|^[^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}])" ) );
125  if ( keyToken.contains( sInvalidRegexp ) )
126  {
127  const QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
128  QgsMessageLog::logMessage( errorString, QString(), Qgis::MessageLevel::Critical );
129  }
130  }
131 
132  return keyTokens;
133 }
134 
135 
136 
146 QgsProjectProperty *findKey_( const QString &scope,
147  const QString &key,
148  QgsProjectPropertyKey &rootProperty )
149 {
150  QgsProjectPropertyKey *currentProperty = &rootProperty;
151  QgsProjectProperty *nextProperty; // link to next property down hierarchy
152 
153  QStringList keySequence = makeKeyTokens_( scope, key );
154 
155  while ( !keySequence.isEmpty() )
156  {
157  // if the current head of the sequence list matches the property name,
158  // then traverse down the property hierarchy
159  if ( keySequence.first() == currentProperty->name() )
160  {
161  // remove front key since we're traversing down a level
162  keySequence.pop_front();
163 
164  if ( 1 == keySequence.count() )
165  {
166  // if we have only one key name left, then return the key found
167  return currentProperty->find( keySequence.front() );
168  }
169  else if ( keySequence.isEmpty() )
170  {
171  // if we're out of keys then the current property is the one we
172  // want; i.e., we're in the rate case of being at the top-most
173  // property node
174  return currentProperty;
175  }
176  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
177  {
178  if ( nextProperty->isKey() )
179  {
180  currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
181  }
182  else if ( nextProperty->isValue() && 1 == keySequence.count() )
183  {
184  // it may be that this may be one of several property value
185  // nodes keyed by QDict string; if this is the last remaining
186  // key token and the next property is a value node, then
187  // that's the situation, so return the currentProperty
188  return currentProperty;
189  }
190  else
191  {
192  // QgsProjectPropertyValue not Key, so return null
193  return nullptr;
194  }
195  }
196  else
197  {
198  // if the next key down isn't found
199  // then the overall key sequence doesn't exist
200  return nullptr;
201  }
202  }
203  else
204  {
205  return nullptr;
206  }
207  }
208 
209  return nullptr;
210 }
211 
212 
213 
223 QgsProjectProperty *addKey_( const QString &scope,
224  const QString &key,
225  QgsProjectPropertyKey *rootProperty,
226  const QVariant &value,
227  bool &propertiesModified )
228 {
229  QStringList keySequence = makeKeyTokens_( scope, key );
230 
231  // cursor through property key/value hierarchy
232  QgsProjectPropertyKey *currentProperty = rootProperty;
233  QgsProjectProperty *nextProperty; // link to next property down hierarchy
234  QgsProjectPropertyKey *newPropertyKey = nullptr;
235 
236  propertiesModified = false;
237  while ( ! keySequence.isEmpty() )
238  {
239  // if the current head of the sequence list matches the property name,
240  // then traverse down the property hierarchy
241  if ( keySequence.first() == currentProperty->name() )
242  {
243  // remove front key since we're traversing down a level
244  keySequence.pop_front();
245 
246  // if key sequence has one last element, then we use that as the
247  // name to store the value
248  if ( 1 == keySequence.count() )
249  {
250  QgsProjectProperty *property = currentProperty->find( keySequence.front() );
251  if ( !property || property->value() != value )
252  {
253  currentProperty->setValue( keySequence.front(), value );
254  propertiesModified = true;
255  }
256 
257  return currentProperty;
258  }
259  // we're at the top element if popping the keySequence element
260  // will leave it empty; in that case, just add the key
261  else if ( keySequence.isEmpty() )
262  {
263  if ( currentProperty->value() != value )
264  {
265  currentProperty->setValue( value );
266  propertiesModified = true;
267  }
268 
269  return currentProperty;
270  }
271  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
272  {
273  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
274 
275  if ( currentProperty )
276  {
277  continue;
278  }
279  else // QgsProjectPropertyValue not Key, so return null
280  {
281  return nullptr;
282  }
283  }
284  else // the next subkey doesn't exist, so add it
285  {
286  if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
287  {
288  currentProperty = newPropertyKey;
289  }
290  continue;
291  }
292  }
293  else
294  {
295  return nullptr;
296  }
297  }
298 
299  return nullptr;
300 }
301 
309 void removeKey_( const QString &scope,
310  const QString &key,
311  QgsProjectPropertyKey &rootProperty )
312 {
313  QgsProjectPropertyKey *currentProperty = &rootProperty;
314 
315  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
316  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
317 
318  QStringList keySequence = makeKeyTokens_( scope, key );
319 
320  while ( ! keySequence.isEmpty() )
321  {
322  // if the current head of the sequence list matches the property name,
323  // then traverse down the property hierarchy
324  if ( keySequence.first() == currentProperty->name() )
325  {
326  // remove front key since we're traversing down a level
327  keySequence.pop_front();
328 
329  // if we have only one key name left, then try to remove the key
330  // with that name
331  if ( 1 == keySequence.count() )
332  {
333  currentProperty->removeKey( keySequence.front() );
334  }
335  // if we're out of keys then the current property is the one we
336  // want to remove, but we can't delete it directly; we need to
337  // delete it from the parent property key container
338  else if ( keySequence.isEmpty() )
339  {
340  previousQgsPropertyKey->removeKey( currentProperty->name() );
341  }
342  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
343  {
344  previousQgsPropertyKey = currentProperty;
345  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
346 
347  if ( currentProperty )
348  {
349  continue;
350  }
351  else // QgsProjectPropertyValue not Key, so return null
352  {
353  return;
354  }
355  }
356  else // if the next key down isn't found
357  {
358  // then the overall key sequence doesn't exist
359  return;
360  }
361  }
362  else
363  {
364  return;
365  }
366  }
367 }
368 
369 QgsProject::QgsProject( QObject *parent, Qgis::ProjectCapabilities capabilities )
370  : QObject( parent )
371  , mCapabilities( capabilities )
372  , mLayerStore( new QgsMapLayerStore( this ) )
373  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
374  , mSnappingConfig( this )
375  , mRelationManager( new QgsRelationManager( this ) )
376  , mAnnotationManager( new QgsAnnotationManager( this ) )
377  , mLayoutManager( new QgsLayoutManager( this ) )
378  , m3DViewsManager( new QgsMapViewsManager( this ) )
379  , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
380  , mSensorManager( new QgsSensorManager( this ) )
381  , mViewSettings( new QgsProjectViewSettings( this ) )
382  , mStyleSettings( new QgsProjectStyleSettings( this ) )
383  , mTimeSettings( new QgsProjectTimeSettings( this ) )
384  , mElevationProperties( new QgsProjectElevationProperties( this ) )
385  , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
386  , mGpsSettings( new QgsProjectGpsSettings( this ) )
387  , mRootGroup( new QgsLayerTree )
388  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
389  , mArchive( new QgsArchive() )
390  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
391 {
392  mProperties.setName( QStringLiteral( "properties" ) );
393 
394  mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
395  mMainAnnotationLayer->setParent( this );
396 
397  clear();
398 
399  // bind the layer tree to the map layer registry.
400  // whenever layers are added to or removed from the registry,
401  // layer tree will be updated
402  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
403  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
404  connect( this, &QgsProject::layersRemoved, this, [this] { cleanTransactionGroups(); } );
405  connect( this, qOverload< const QList<QgsMapLayer *> & >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
406 
407  // proxy map layer store signals to this
408  connect( mLayerStore.get(), qOverload<const QStringList &>( &QgsMapLayerStore::layersWillBeRemoved ),
409  this, [this]( const QStringList & layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
410  connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ),
411  this, [this]( const QList<QgsMapLayer *> &layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
412  connect( mLayerStore.get(), qOverload< const QString & >( &QgsMapLayerStore::layerWillBeRemoved ),
413  this, [this]( const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
414  connect( mLayerStore.get(), qOverload< QgsMapLayer * >( &QgsMapLayerStore::layerWillBeRemoved ),
415  this, [this]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
416  connect( mLayerStore.get(), qOverload<const QStringList & >( &QgsMapLayerStore::layersRemoved ), this,
417  [this]( const QStringList & layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
418  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this,
419  [this]( const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
420  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this,
421  [this]() { mProjectScope.reset(); emit removeAll(); } );
422  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this,
423  [this]( const QList< QgsMapLayer * > &layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
424  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this,
425  [this]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
426 
427  if ( QgsApplication::instance() )
428  {
430  }
431 
432  connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ), this,
433  [this]( const QList<QgsMapLayer *> &layers )
434  {
435  for ( const auto &layer : layers )
436  {
437  disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
438  }
439  }
440  );
441  connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersAdded ), this,
442  [this]( const QList<QgsMapLayer *> &layers )
443  {
444  for ( const auto &layer : layers )
445  {
446  connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
447  }
448  }
449  );
450 
454 
455  mStyleSettings->combinedStyleModel()->addDefaultStyle();
456 }
457 
458 
460 {
461  mIsBeingDeleted = true;
462 
463  clear();
464  releaseHandlesToProjectArchive();
465  delete mBadLayerHandler;
466  delete mRelationManager;
467  delete mLayerTreeRegistryBridge;
468  delete mRootGroup;
469  if ( this == sProject )
470  {
471  sProject = nullptr;
472  }
473 }
474 
476 {
477  sProject = project;
478 }
479 
480 
482 {
483  if ( !sProject )
484  {
485  sProject = new QgsProject;
486 
488  }
489  return sProject;
490 }
491 
492 void QgsProject::setTitle( const QString &title )
493 {
495 
496  if ( title == mMetadata.title() )
497  return;
498 
499  mMetadata.setTitle( title );
500  mProjectScope.reset();
501  emit metadataChanged();
502 
503  setDirty( true );
504 }
505 
506 QString QgsProject::title() const
507 {
508  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
510 
511  return mMetadata.title();
512 }
513 
515 {
517 
518  const bool oldEvaluateDefaultValues = mFlags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
519  const bool newEvaluateDefaultValues = flags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
520  if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
521  {
522  const QMap<QString, QgsMapLayer *> layers = mapLayers();
523  for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
524  {
525  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
526  if ( vl->dataProvider() )
527  vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, newEvaluateDefaultValues );
528  }
529  }
530 
531  const bool oldTrustLayerMetadata = mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
532  const bool newTrustLayerMetadata = flags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
533  if ( oldTrustLayerMetadata != newTrustLayerMetadata )
534  {
535  const QMap<QString, QgsMapLayer *> layers = mapLayers();
536  for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
537  {
538  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
539  {
540  vl->setReadExtentFromXml( newTrustLayerMetadata );
541  }
542  }
543  }
544 
545  if ( mFlags != flags )
546  {
547  mFlags = flags;
548  setDirty( true );
549  }
550 }
551 
552 void QgsProject::setFlag( Qgis::ProjectFlag flag, bool enabled )
553 {
555 
556  Qgis::ProjectFlags newFlags = mFlags;
557  if ( enabled )
558  newFlags |= flag;
559  else
560  newFlags &= ~( static_cast< int >( flag ) );
561  setFlags( newFlags );
562 }
563 
564 QString QgsProject::saveUser() const
565 {
567 
568  return mSaveUser;
569 }
570 
572 {
574 
575  return mSaveUserFull;
576 }
577 
579 {
581 
582  return mSaveDateTime;
583 }
584 
586 {
588 
589  return mSaveVersion;
590 }
591 
593 {
595 
596  return mDirty;
597 }
598 
599 void QgsProject::setDirty( const bool dirty )
600 {
602 
603  if ( dirty && mDirtyBlockCount > 0 )
604  return;
605 
606  if ( dirty )
607  emit dirtySet();
608 
609  if ( mDirty == dirty )
610  return;
611 
612  mDirty = dirty;
613  emit isDirtyChanged( mDirty );
614 }
615 
616 void QgsProject::setPresetHomePath( const QString &path )
617 {
619 
620  if ( path == mHomePath )
621  return;
622 
623  mHomePath = path;
624  mCachedHomePath.clear();
625  mProjectScope.reset();
626 
627  emit homePathChanged();
628 
629  setDirty( true );
630 }
631 
632 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
633 {
635 
636  const QList<QgsAttributeEditorElement *> elements = parent->children();
637 
638  for ( QgsAttributeEditorElement *element : elements )
639  {
640  if ( element->type() == Qgis::AttributeEditorType::Container )
641  {
642  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
643 
644  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
645 
646  if ( !container->children().empty() )
647  registerTranslatableContainers( translationContext, container, layerId );
648  }
649  }
650 }
651 
653 {
655 
656  //register layers
657  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
658 
659  for ( const QgsLayerTreeLayer *layer : layers )
660  {
661  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
662 
663  QgsMapLayer *mapLayer = layer->layer();
665  {
666  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
667 
668  //register aliases and fields
669  const QgsFields fields = vlayer->fields();
670  for ( const QgsField &field : fields )
671  {
672  QString fieldName;
673  if ( field.alias().isEmpty() )
674  fieldName = field.name();
675  else
676  fieldName = field.alias();
677 
678  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
679 
680  if ( field.editorWidgetSetup().type() == QLatin1String( "ValueRelation" ) )
681  {
682  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
683  }
684  }
685 
686  //register formcontainers
687  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
688 
689  }
690  }
691 
692  //register layergroups
693  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
694  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
695  {
696  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
697  }
698 
699  //register relations
700  const QList<QgsRelation> &relations = mRelationManager->relations().values();
701  for ( const QgsRelation &relation : relations )
702  {
703  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
704  }
705 }
706 
708 {
710 
711  mDataDefinedServerProperties = properties;
712 }
713 
715 {
717 
718  return mDataDefinedServerProperties;
719 }
720 
722 {
724 
725  switch ( mTransactionMode )
726  {
729  {
730  if ( ! vectorLayer )
731  return false;
732  return vectorLayer->startEditing();
733  }
734 
736  return mEditBufferGroup.startEditing();
737  }
738 
739  return false;
740 }
741 
742 bool QgsProject::commitChanges( QStringList &commitErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
743 {
745 
746  switch ( mTransactionMode )
747  {
750  {
751  if ( ! vectorLayer )
752  {
753  commitErrors.append( tr( "Trying to commit changes without a layer specified. This only works if the transaction mode is buffered" ) );
754  return false;
755  }
756  bool success = vectorLayer->commitChanges( stopEditing );
757  commitErrors = vectorLayer->commitErrors();
758  return success;
759  }
760 
762  return mEditBufferGroup.commitChanges( commitErrors, stopEditing );
763  }
764 
765  return false;
766 }
767 
768 bool QgsProject::rollBack( QStringList &rollbackErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
769 {
771 
772  switch ( mTransactionMode )
773  {
776  {
777  if ( ! vectorLayer )
778  {
779  rollbackErrors.append( tr( "Trying to roll back changes without a layer specified. This only works if the transaction mode is buffered" ) );
780  return false;
781  }
782  bool success = vectorLayer->rollBack( stopEditing );
783  rollbackErrors = vectorLayer->commitErrors();
784  return success;
785  }
786 
788  return mEditBufferGroup.rollBack( rollbackErrors, stopEditing );
789  }
790 
791  return false;
792 }
793 
794 void QgsProject::setFileName( const QString &name )
795 {
797 
798  if ( name == mFile.fileName() )
799  return;
800 
801  const QString oldHomePath = homePath();
802 
803  mFile.setFileName( name );
804  mCachedHomePath.clear();
805  mProjectScope.reset();
806 
807  emit fileNameChanged();
808 
809  const QString newHomePath = homePath();
810  if ( newHomePath != oldHomePath )
811  emit homePathChanged();
812 
813  setDirty( true );
814 }
815 
816 QString QgsProject::fileName() const
817 {
818  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
820 
821  return mFile.fileName();
822 }
823 
824 void QgsProject::setOriginalPath( const QString &path )
825 {
827 
828  mOriginalPath = path;
829 }
830 
832 {
834 
835  return mOriginalPath;
836 }
837 
838 QFileInfo QgsProject::fileInfo() const
839 {
841 
842  return QFileInfo( mFile );
843 }
844 
846 {
847  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
849 
851 }
852 
853 QDateTime QgsProject::lastModified() const
854 {
856 
857  if ( QgsProjectStorage *storage = projectStorage() )
858  {
860  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
861  return metadata.lastModified;
862  }
863  else
864  {
865  return QFileInfo( mFile.fileName() ).lastModified();
866  }
867 }
868 
870 {
872 
873  if ( projectStorage() )
874  return QString();
875 
876  if ( mFile.fileName().isEmpty() )
877  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
878 
879  return QFileInfo( mFile.fileName() ).absolutePath();
880 }
881 
883 {
884  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
886 
887  if ( projectStorage() )
888  return QString();
889 
890  if ( mFile.fileName().isEmpty() )
891  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
892 
893  return QFileInfo( mFile.fileName() ).absoluteFilePath();
894 }
895 
896 QString QgsProject::baseName() const
897 {
898  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
900 
901  if ( QgsProjectStorage *storage = projectStorage() )
902  {
904  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
905  return metadata.name;
906  }
907  else
908  {
909  return QFileInfo( mFile.fileName() ).completeBaseName();
910  }
911 }
912 
914 {
916 
917  const bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
919 }
920 
922 {
924 
925  switch ( type )
926  {
928  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
929  break;
931  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
932  break;
933  }
934 }
935 
937 {
938  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
940 
941  return mCrs;
942 }
943 
944 void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
945 {
947 
948  if ( crs != mCrs )
949  {
950  mCrs = crs;
951  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
952  mProjectScope.reset();
953 
954  // if annotation layer doesn't have a crs (i.e. in a newly created project), it should
955  // initially inherit the project CRS
956  if ( !mMainAnnotationLayer->crs().isValid() || mMainAnnotationLayer->isEmpty() )
957  mMainAnnotationLayer->setCrs( crs );
958 
959  setDirty( true );
960  emit crsChanged();
961  }
962 
963  if ( adjustEllipsoid )
965 }
966 
967 QString QgsProject::ellipsoid() const
968 {
969  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
971 
972  if ( !crs().isValid() )
973  return geoNone();
974 
975  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), geoNone() );
976 }
977 
978 void QgsProject::setEllipsoid( const QString &ellipsoid )
979 {
981 
982  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
983  return;
984 
985  mProjectScope.reset();
986  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
987  emit ellipsoidChanged( ellipsoid );
988 }
989 
991 {
992  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
994 
995  return mTransformContext;
996 }
997 
999 {
1001 
1002  if ( context == mTransformContext )
1003  return;
1004 
1005  mTransformContext = context;
1006  mProjectScope.reset();
1007 
1008  mMainAnnotationLayer->setTransformContext( context );
1009  for ( auto &layer : mLayerStore.get()->mapLayers() )
1010  {
1011  layer->setTransformContext( context );
1012  }
1013  emit transformContextChanged();
1014 }
1015 
1017 {
1019 
1020  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
1021 
1022  emit aboutToBeCleared();
1023 
1024  mProjectScope.reset();
1025  mFile.setFileName( QString() );
1026  mProperties.clearKeys();
1027  mSaveUser.clear();
1028  mSaveUserFull.clear();
1029  mSaveDateTime = QDateTime();
1030  mSaveVersion = QgsProjectVersion();
1031  mHomePath.clear();
1032  mCachedHomePath.clear();
1033  mTransactionMode = Qgis::TransactionMode::Disabled;
1034  mFlags = Qgis::ProjectFlags();
1035  mDirty = false;
1036  mCustomVariables.clear();
1038  mMetadata = QgsProjectMetadata();
1039  mElevationShadingRenderer = QgsElevationShadingRenderer();
1040  if ( !mSettings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
1041  {
1042  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
1043  mMetadata.setAuthor( QgsApplication::userFullName() );
1044  }
1045  emit metadataChanged();
1046 
1048  context.readSettings();
1049  setTransformContext( context );
1050 
1051  //fallback to QGIS default measurement unit
1052  bool ok = false;
1053  const Qgis::DistanceUnit distanceUnit = QgsUnitTypes::decodeDistanceUnit( mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
1054  setDistanceUnits( ok ? distanceUnit : Qgis::DistanceUnit::Meters );
1055  ok = false;
1056  const Qgis::AreaUnit areaUnits = QgsUnitTypes::decodeAreaUnit( mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
1058 
1059  mEmbeddedLayers.clear();
1060  mRelationManager->clear();
1061  mAnnotationManager->clear();
1062  mLayoutManager->clear();
1063  m3DViewsManager->clear();
1064  mBookmarkManager->clear();
1065  mSensorManager->clear();
1066  mViewSettings->reset();
1067  mTimeSettings->reset();
1068  mElevationProperties->reset();
1069  mDisplaySettings->reset();
1070  mGpsSettings->reset();
1071  mSnappingConfig.reset();
1072  mAvoidIntersectionsMode = Qgis::AvoidIntersectionsMode::AllowIntersections;
1075 
1076  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1078 
1079  mLabelingEngineSettings->clear();
1080 
1081  // must happen BEFORE archive reset, because we need to release the hold on any files which
1082  // exists within the archive. Otherwise the archive can't be removed.
1083  releaseHandlesToProjectArchive();
1084 
1085  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
1086  mArchive.reset( new QgsArchive() );
1087 
1088  // must happen AFTER archive reset, as it will populate a new style database within the new archive
1089  mStyleSettings->reset();
1090 
1092 
1093  if ( !mIsBeingDeleted )
1094  {
1095  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
1096  emit projectColorsChanged();
1097  }
1098 
1099  // reset some default project properties
1100  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
1101  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
1102  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
1103 
1104  const bool defaultRelativePaths = mSettings.value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true ).toBool();
1106 
1107  int red = mSettings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
1108  int green = mSettings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
1109  int blue = mSettings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
1110  setBackgroundColor( QColor( red, green, blue ) );
1111 
1112  red = mSettings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
1113  green = mSettings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
1114  blue = mSettings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
1115  const int alpha = mSettings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
1116  setSelectionColor( QColor( red, green, blue, alpha ) );
1117 
1118  mSnappingConfig.clearIndividualLayerSettings();
1119 
1121  mRootGroup->clear();
1122  if ( mMainAnnotationLayer )
1123  mMainAnnotationLayer->reset();
1124 
1125  snapSingleBlocker.release();
1126 
1127  if ( !mBlockSnappingUpdates )
1128  emit snappingConfigChanged( mSnappingConfig );
1129 
1130  setDirty( false );
1131  emit homePathChanged();
1132  emit cleared();
1133 }
1134 
1135 // basically a debugging tool to dump property list values
1136 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
1137 {
1138  QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
1139  topQgsPropertyKey.dump();
1140 }
1141 
1170 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
1171 {
1172  const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1173 
1174  if ( propertiesElem.isNull() ) // no properties found, so we're done
1175  {
1176  return;
1177  }
1178 
1179  const QDomNodeList scopes = propertiesElem.childNodes();
1180 
1181  if ( propertiesElem.firstChild().isNull() )
1182  {
1183  QgsDebugError( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
1184  return;
1185  }
1186 
1187  if ( ! project_properties.readXml( propertiesElem ) )
1188  {
1189  QgsDebugError( QStringLiteral( "Project_properties.readXml() failed" ) );
1190  }
1191 }
1192 
1199 QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
1200 {
1201  QgsPropertyCollection ddServerProperties;
1202  // Read data defined server properties
1203  const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral( "dataDefinedServerProperties" ) );
1204  if ( !ddElem.isNull() )
1205  {
1206  if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1207  {
1208  QgsDebugError( QStringLiteral( "dataDefinedServerProperties.readXml() failed" ) );
1209  }
1210  }
1211  return ddServerProperties;
1212 }
1213 
1218 static void _getTitle( const QDomDocument &doc, QString &title )
1219 {
1220  const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral( "title" ) );
1221 
1222  title.clear(); // by default the title will be empty
1223 
1224  if ( titleNode.isNull() )
1225  {
1226  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1227  return;
1228  }
1229 
1230  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1231  {
1232  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1233  return;
1234  }
1235 
1236  const QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1237 
1238  if ( !titleTextNode.isText() )
1239  {
1240  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1241  return;
1242  }
1243 
1244  const QDomText titleText = titleTextNode.toText();
1245 
1246  title = titleText.data();
1247 
1248 }
1249 
1250 static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1251 {
1252  const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1253 
1254  if ( !nl.count() )
1255  {
1256  QgsDebugError( QStringLiteral( "unable to find qgis element" ) );
1257  return;
1258  }
1259 
1260  const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1261 
1262  const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1263  lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
1264  lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
1265  lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral( "saveDateTime" ), QString() ), Qt::ISODate );
1266 }
1267 
1268 QgsProjectVersion getVersion( const QDomDocument &doc )
1269 {
1270  const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1271 
1272  if ( !nl.count() )
1273  {
1274  QgsDebugError( QStringLiteral( " unable to find qgis element in project file" ) );
1275  return QgsProjectVersion( 0, 0, 0, QString() );
1276  }
1277 
1278  const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1279 
1280  const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1281  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
1282  return projectVersion;
1283 }
1284 
1286 {
1288 
1289  return mSnappingConfig;
1290 }
1291 
1293 {
1295 
1296  if ( mSnappingConfig == snappingConfig )
1297  return;
1298 
1299  mSnappingConfig = snappingConfig;
1300  setDirty( true );
1301  emit snappingConfigChanged( mSnappingConfig );
1302 }
1303 
1305 {
1307 
1308  if ( mAvoidIntersectionsMode == mode )
1309  return;
1310 
1311  mAvoidIntersectionsMode = mode;
1313 }
1314 
1315 static QgsMapLayer::ReadFlags projectFlagsToLayerReadFlags( Qgis::ProjectReadFlags projectReadFlags, Qgis::ProjectFlags projectFlags )
1316 {
1318  if ( projectReadFlags & Qgis::ProjectReadFlag::DontResolveLayers )
1319  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1320  // Propagate trust layer metadata flag
1321  if ( ( projectFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( projectReadFlags & Qgis::ProjectReadFlag::TrustLayerMetadata ) )
1323  // Propagate open layers in read-only mode
1324  if ( ( projectReadFlags & Qgis::ProjectReadFlag::ForceReadOnlyLayers ) )
1325  layerFlags |= QgsMapLayer::FlagForceReadOnly;
1326 
1327  return layerFlags;
1328 }
1329 
1331 {
1332  QString layerId;
1333  QString provider;
1334  QString dataSource;
1337  QDomElement layerElement;
1338 };
1339 
1340 void QgsProject::preloadProviders( const QVector<QDomNode> &parallelLayerNodes,
1341  const QgsReadWriteContext &context,
1342  QMap<QString, QgsDataProvider *> &loadedProviders,
1343  QgsMapLayer::ReadFlags layerReadFlags,
1344  int totalProviderCount )
1345 {
1346  int i = 0;
1347  QEventLoop loop;
1348 
1349  QMap<QString, LayerToLoad> layersToLoad;
1350 
1351  for ( const QDomNode &node : parallelLayerNodes )
1352  {
1353  LayerToLoad layerToLoad;
1354 
1355  const QDomElement layerElement = node.toElement();
1356  layerToLoad.layerElement = layerElement;
1357  layerToLoad.layerId = layerElement.namedItem( QStringLiteral( "id" ) ).toElement().text();
1358  layerToLoad.provider = layerElement.namedItem( QStringLiteral( "provider" ) ).toElement().text();
1359  layerToLoad.dataSource = layerElement.namedItem( QStringLiteral( "datasource" ) ).toElement().text();
1360 
1361  layerToLoad.dataSource = QgsProviderRegistry::instance()->relativeToAbsoluteUri( layerToLoad.provider, layerToLoad.dataSource, context );
1362 
1363  layerToLoad.options = QgsDataProvider::ProviderOptions( {context.transformContext()} );
1364  layerToLoad.flags = QgsMapLayer::providerReadFlags( node, layerReadFlags );
1365 
1366  // Requesting credential from worker thread could lead to deadlocks because the main thread is waiting for worker thread to fininsh
1367  layerToLoad.flags.setFlag( QgsDataProvider::SkipCredentialsRequest, true );
1368  layerToLoad.flags.setFlag( QgsDataProvider::ParallelThreadLoading, true );
1369 
1370  layersToLoad.insert( layerToLoad.layerId, layerToLoad );
1371  }
1372 
1373  while ( !layersToLoad.isEmpty() )
1374  {
1375  const QList<LayerToLoad> layersToAttemptInParallel = layersToLoad.values();
1376  QString layerToAttemptInMainThread;
1377 
1378  QHash<QString, QgsRunnableProviderCreator *> runnables;
1379  QThreadPool threadPool;
1380  threadPool.setMaxThreadCount( QgsSettingsRegistryCore::settingsLayerParallelLoadingMaxCount->value() );
1381 
1382  for ( const LayerToLoad &lay : layersToAttemptInParallel )
1383  {
1384  QgsRunnableProviderCreator *run = new QgsRunnableProviderCreator( lay.layerId, lay.provider, lay.dataSource, lay.options, lay.flags );
1385  runnables.insert( lay.layerId, run );
1386 
1387  QObject::connect( run, &QgsRunnableProviderCreator::providerCreated, run, [&]( bool isValid, const QString & layId )
1388  {
1389  if ( isValid )
1390  {
1391  layersToLoad.remove( layId );
1392  i++;
1393  QgsRunnableProviderCreator *finishedRun = runnables.value( layId, nullptr );
1394  Q_ASSERT( finishedRun );
1395 
1396  std::unique_ptr<QgsDataProvider> provider( finishedRun->dataProvider() );
1397  Q_ASSERT( provider && provider->isValid() );
1398 
1399  loadedProviders.insert( layId, provider.release() );
1400  emit layerLoaded( i, totalProviderCount );
1401  }
1402  else
1403  {
1404  if ( layerToAttemptInMainThread.isEmpty() )
1405  layerToAttemptInMainThread = layId;
1406  threadPool.clear(); //we have to stop all loading provider to try this layer in main thread and maybe have credentials
1407  }
1408 
1409  if ( i == parallelLayerNodes.count() || !isValid )
1410  loop.quit();
1411  } );
1412  threadPool.start( run );
1413  }
1414  loop.exec();
1415 
1416  threadPool.waitForDone(); // to be sure all threads are finished
1417 
1418  qDeleteAll( runnables );
1419 
1420  // We try with the first layer returned invalid but this time in the main thread to maybe have credentials and continue with others not loaded in parallel
1421  auto it = layersToLoad.find( layerToAttemptInMainThread );
1422  if ( it != layersToLoad.end() )
1423  {
1424  std::unique_ptr<QgsDataProvider> provider;
1425  QString layerId;
1426  {
1427  const LayerToLoad &lay = it.value();
1428  QgsDataProvider::ReadFlags providerFlags = lay.flags;
1429  providerFlags.setFlag( QgsDataProvider::SkipCredentialsRequest, false );
1430  providerFlags.setFlag( QgsDataProvider::ParallelThreadLoading, false );
1431  QgsScopedRuntimeProfile profile( "Create data providers/" + lay.layerId, QStringLiteral( "projectload" ) );
1432  provider.reset( QgsProviderRegistry::instance()->createProvider( lay.provider, lay.dataSource, lay.options, providerFlags ) );
1433  i++;
1434  if ( provider && provider->isValid() )
1435  {
1436  emit layerLoaded( i, totalProviderCount );
1437  }
1438  layerId = lay.layerId;
1439  layersToLoad.erase( it );
1440  // can't access "lay" anymore -- it's now been freed
1441  }
1442  loadedProviders.insert( layerId, provider.release() );
1443  }
1444 
1445  // if there still are some not loaded providers or some invalid in parallel thread we start again
1446  }
1447 
1448 }
1449 
1450 void QgsProject::releaseHandlesToProjectArchive()
1451 {
1452  mStyleSettings->removeProjectStyle();
1453 }
1454 
1455 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, Qgis::ProjectReadFlags flags )
1456 {
1458 
1459  // Layer order is set by the restoring the legend settings from project file.
1460  // This is done on the 'readProject( ... )' signal
1461 
1462  QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
1463 
1464  // process the map layer nodes
1465 
1466  if ( layerElement.isNull() ) // if we have no layers to process, bail
1467  {
1468  return true; // Decided to return "true" since it's
1469  // possible for there to be a project with no
1470  // layers; but also, more imporantly, this
1471  // would cause the tests/qgsproject to fail
1472  // since the test suite doesn't currently
1473  // support test layers
1474  }
1475 
1476  bool returnStatus = true;
1477  int numLayers = 0;
1478 
1479  while ( ! layerElement.isNull() )
1480  {
1481  numLayers++;
1482  layerElement = layerElement.nextSiblingElement( QStringLiteral( "maplayer" ) );
1483  }
1484 
1485  // order layers based on their dependencies
1486  QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), QStringLiteral( "projectload" ) );
1487  const QgsLayerDefinition::DependencySorter depSorter( doc );
1488  if ( depSorter.hasCycle() )
1489  return false;
1490 
1491  // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1492  if ( depSorter.hasMissingDependency() )
1493  returnStatus = false;
1494 
1495  emit layerLoaded( 0, numLayers );
1496 
1497  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1498  const int totalLayerCount = sortedLayerNodes.count();
1499 
1500  QVector<QDomNode> parallelLoading;
1501  QMap<QString, QgsDataProvider *> loadedProviders;
1502 
1504  {
1505  profile.switchTask( tr( "Load providers in parallel" ) );
1506  for ( const QDomNode &node : sortedLayerNodes )
1507  {
1508  const QDomElement element = node.toElement();
1509  if ( element.attribute( QStringLiteral( "embedded" ) ) != QLatin1String( "1" ) )
1510  {
1511  const QString layerId = node.namedItem( QStringLiteral( "id" ) ).toElement().text();
1512  if ( !depSorter.isLayerDependent( layerId ) )
1513  {
1514  const QDomNode mnl = element.namedItem( QStringLiteral( "provider" ) );
1515  const QDomElement mne = mnl.toElement();
1516  const QString provider = mne.text();
1518  if ( meta && meta->providerCapabilities().testFlag( QgsProviderMetadata::ParallelCreateProvider ) )
1519  {
1520  parallelLoading.append( node );
1521  continue;
1522  }
1523  }
1524  }
1525  }
1526 
1527  QgsReadWriteContext context;
1528  context.setPathResolver( pathResolver() );
1529  if ( !parallelLoading.isEmpty() )
1530  preloadProviders( parallelLoading, context, loadedProviders, projectFlagsToLayerReadFlags( flags, mFlags ), sortedLayerNodes.count() );
1531  }
1532 
1533  int i = loadedProviders.count();
1534  for ( const QDomNode &node : std::as_const( sortedLayerNodes ) )
1535  {
1536  const QDomElement element = node.toElement();
1537  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1538  if ( !name.isNull() )
1539  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1540 
1541  profile.switchTask( name );
1542  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1543  {
1544  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1545  }
1546  else
1547  {
1548  QgsReadWriteContext context;
1549  context.setPathResolver( pathResolver() );
1550  context.setProjectTranslator( this );
1552  QString layerId = element.namedItem( QStringLiteral( "id" ) ).toElement().text();
1553 
1554  if ( !addLayer( element, brokenNodes, context, flags, loadedProviders.take( layerId ) ) )
1555  {
1556  returnStatus = false;
1557  }
1558  const auto messages = context.takeMessages();
1559  if ( !messages.isEmpty() )
1560  {
1561  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1562  }
1563  }
1564  emit layerLoaded( i + 1, totalLayerCount );
1565  i++;
1566  }
1567 
1568  return returnStatus;
1569 }
1570 
1571 bool QgsProject::addLayer( const QDomElement &layerElem,
1572  QList<QDomNode> &brokenNodes,
1573  QgsReadWriteContext &context,
1574  Qgis::ProjectReadFlags flags,
1575  QgsDataProvider *provider )
1576 {
1578 
1579  const QString type = layerElem.attribute( QStringLiteral( "type" ) );
1580  QgsDebugMsgLevel( "Layer type is " + type, 4 );
1581  std::unique_ptr<QgsMapLayer> mapLayer;
1582 
1583  QgsScopedRuntimeProfile profile( tr( "Create layer" ), QStringLiteral( "projectload" ) );
1584 
1585  bool ok = false;
1586  const Qgis::LayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1587  if ( !ok )
1588  {
1589  QgsDebugError( QStringLiteral( "Unknown layer type \"%1\"" ).arg( type ) );
1590  return false;
1591  }
1592 
1593  switch ( layerType )
1594  {
1596  mapLayer = std::make_unique<QgsVectorLayer>();
1597  break;
1598 
1600  mapLayer = std::make_unique<QgsRasterLayer>();
1601  break;
1602 
1603  case Qgis::LayerType::Mesh:
1604  mapLayer = std::make_unique<QgsMeshLayer>();
1605  break;
1606 
1608  mapLayer = std::make_unique<QgsVectorTileLayer>();
1609  break;
1610 
1612  mapLayer = std::make_unique<QgsPointCloudLayer>();
1613  break;
1614 
1616  mapLayer = std::make_unique<QgsTiledSceneLayer>();
1617  break;
1618 
1620  {
1621  const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1622  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1623  break;
1624  }
1625 
1627  {
1628  const QgsAnnotationLayer::LayerOptions options( mTransformContext );
1629  mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1630  break;
1631  }
1632 
1634  {
1635  const QgsGroupLayer::LayerOptions options( mTransformContext );
1636  mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1637  break;
1638  }
1639  }
1640 
1641  if ( !mapLayer )
1642  {
1643  QgsDebugError( QStringLiteral( "Unable to create layer" ) );
1644  return false;
1645  }
1646 
1647  Q_CHECK_PTR( mapLayer ); // NOLINT
1648 
1649  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1650  // because if it was, the newly created layer will not be added to the store and it would leak.
1651  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1652  Q_ASSERT( ! layerId.isEmpty() );
1653  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1654 
1655  // have the layer restore state that is stored in Dom node
1656  QgsMapLayer::ReadFlags layerFlags = projectFlagsToLayerReadFlags( flags, mFlags );
1657 
1658  profile.switchTask( tr( "Load layer source" ) );
1659  const bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags, provider ) && mapLayer->isValid();
1660 
1661  // apply specific settings to vector layer
1662  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1663  {
1664  vl->setReadExtentFromXml( ( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( flags & Qgis::ProjectReadFlag::TrustLayerMetadata ) );
1665  if ( vl->dataProvider() )
1666  {
1668  vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, evaluateDefaultValues );
1669  }
1670  }
1671 
1672  profile.switchTask( tr( "Add layer to project" ) );
1673  QList<QgsMapLayer *> newLayers;
1674  newLayers << mapLayer.get();
1675  if ( layerIsValid || flags & Qgis::ProjectReadFlag::DontResolveLayers )
1676  {
1677  emit readMapLayer( mapLayer.get(), layerElem );
1678  addMapLayers( newLayers );
1679  // Try to resolve references here (this is necessary to set up joined fields that will be possibly used by
1680  // virtual layers that point to this layer's joined field in their query otherwise they won't be valid ),
1681  // a second attempt to resolve references will be done after all layers are loaded
1682  // see https://github.com/qgis/QGIS/issues/46834
1683  if ( QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1684  {
1685  vLayer->joinBuffer()->resolveReferences( this );
1686  }
1687  }
1688  else
1689  {
1690  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1691  addMapLayers( newLayers, false );
1692  newLayers.first();
1693  QgsDebugError( "Unable to load " + type + " layer" );
1694  brokenNodes.push_back( layerElem );
1695  }
1696 
1697  const bool wasEditable = layerElem.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
1698  if ( wasEditable )
1699  {
1700  mapLayer->setCustomProperty( QStringLiteral( "_layer_was_editable" ), true );
1701  }
1702  else
1703  {
1704  mapLayer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
1705  }
1706 
1707  // It should be safe to delete the layer now if layer was stored, because all the store
1708  // had to to was to reset the data source in case the validity changed.
1709  if ( ! layerWasStored )
1710  {
1711  mapLayer.release();
1712  }
1713 
1714  return layerIsValid;
1715 }
1716 
1717 bool QgsProject::read( const QString &filename, Qgis::ProjectReadFlags flags )
1718 {
1720 
1721  mFile.setFileName( filename );
1722  mCachedHomePath.clear();
1723  mProjectScope.reset();
1724 
1725  return read( flags );
1726 }
1727 
1729 {
1731 
1732  const QString filename = mFile.fileName();
1733  bool returnValue;
1734 
1735  if ( QgsProjectStorage *storage = projectStorage() )
1736  {
1737  QTemporaryFile inDevice;
1738  if ( !inDevice.open() )
1739  {
1740  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1741  return false;
1742  }
1743 
1744  QgsReadWriteContext context;
1745  context.setProjectTranslator( this );
1746  if ( !storage->readProject( filename, &inDevice, context ) )
1747  {
1748  QString err = tr( "Unable to open %1" ).arg( filename );
1749  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1750  if ( !messages.isEmpty() )
1751  err += QStringLiteral( "\n\n" ) + messages.last().message();
1752  setError( err );
1753  return false;
1754  }
1755  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1756  }
1757  else
1758  {
1759  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1760  {
1761  returnValue = unzip( mFile.fileName(), flags );
1762  }
1763  else
1764  {
1765  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1766  const QFileInfo finfo( mFile.fileName() );
1767  const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1768  if ( QFile( attachmentsZip ).exists() )
1769  {
1770  std::unique_ptr<QgsArchive> archive( new QgsArchive() );
1771  if ( archive->unzip( attachmentsZip ) )
1772  {
1773  releaseHandlesToProjectArchive();
1774  mArchive = std::move( archive );
1775  }
1776  }
1777  returnValue = readProjectFile( mFile.fileName(), flags );
1778  }
1779 
1780  //on translation we should not change the filename back
1781  if ( !mTranslator )
1782  {
1783  mFile.setFileName( filename );
1784  mCachedHomePath.clear();
1785  mProjectScope.reset();
1786  }
1787  else
1788  {
1789  //but delete the translator
1790  mTranslator.reset( nullptr );
1791  }
1792  }
1793  emit homePathChanged();
1794  return returnValue;
1795 }
1796 
1797 bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlags flags )
1798 {
1800 
1801  // avoid multiple emission of snapping updated signals
1802  ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
1803 
1804  QFile projectFile( filename );
1805  clearError();
1806 
1807  QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
1808  QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), QStringLiteral( "projectload" ) );
1809 
1810  const QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), QgsApplication::settingsLocaleUserLocale->value() );
1811 
1812  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1813  {
1814  mTranslator.reset( new QTranslator() );
1815  ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1816  }
1817 
1818  profile.switchTask( tr( "Reading project file" ) );
1819  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1820 
1821  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1822  {
1823  projectFile.close();
1824 
1825  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1826 
1827  return false;
1828  }
1829 
1830  QTextStream textStream( &projectFile );
1831 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1832  textStream.setCodec( "UTF-8" );
1833 #endif
1834  QString projectString = textStream.readAll();
1835  projectFile.close();
1836 
1837  for ( int i = 0; i < 32; i++ )
1838  {
1839  if ( i == 9 || i == 10 || i == 13 )
1840  {
1841  continue;
1842  }
1843  projectString.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
1844  }
1845 
1846  // location of problem associated with errorMsg
1847  int line, column;
1848  QString errorMsg;
1849  if ( !doc->setContent( projectString, &errorMsg, &line, &column ) )
1850  {
1851  const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1852  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1853  QgsDebugError( errorString );
1854  setError( errorString );
1855 
1856  return false;
1857  }
1858 
1859  projectFile.close();
1860 
1861  QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1862 
1863  // get project version string, if any
1864  const QgsProjectVersion fileVersion = getVersion( *doc );
1865  const QgsProjectVersion thisVersion( Qgis::version() );
1866 
1867  profile.switchTask( tr( "Updating project file" ) );
1868  if ( thisVersion > fileVersion )
1869  {
1870  const bool isOlderMajorVersion = fileVersion.majorVersion() < thisVersion.majorVersion();
1871 
1872  if ( isOlderMajorVersion )
1873  {
1874  QgsLogger::warning( "Loading a file that was saved with an older "
1875  "version of qgis (saved in " + fileVersion.text() +
1876  ", loaded in " + Qgis::version() +
1877  "). Problems may occur." );
1878  }
1879 
1880  QgsProjectFileTransform projectFile( *doc, fileVersion );
1881 
1882  // Shows a warning when an old project file is read.
1884  emit oldProjectVersionWarning( fileVersion.text() );
1886  emit readVersionMismatchOccurred( fileVersion.text() );
1887 
1888  projectFile.updateRevision( thisVersion );
1889  }
1890  else if ( fileVersion > thisVersion )
1891  {
1892  QgsLogger::warning( "Loading a file that was saved with a newer "
1893  "version of qgis (saved in " + fileVersion.text() +
1894  ", loaded in " + Qgis::version() +
1895  "). Problems may occur." );
1896 
1897  emit readVersionMismatchOccurred( fileVersion.text() );
1898  }
1899 
1900  // start new project, just keep the file name and auxiliary storage
1901  profile.switchTask( tr( "Creating auxiliary storage" ) );
1902  const QString fileName = mFile.fileName();
1903 
1904 
1905  // NOTE [ND] -- I suspect this is wrong, as the archive may contain any number of non-auxiliary
1906  // storage related files from the previously loaded project.
1907  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1908  std::unique_ptr<QgsArchive> archive = std::move( mArchive );
1909 
1910  clear();
1911  // this is ugly, but clear() will have created a new archive and started populating it. We
1912  // need to release handles to this archive now as the subsequent call to move will need
1913  // to delete it, and requires free access to do so.
1914  releaseHandlesToProjectArchive();
1915 
1916  mAuxiliaryStorage = std::move( aStorage );
1917  mArchive = std::move( archive );
1918 
1919 
1920 
1921  mFile.setFileName( fileName );
1922  mCachedHomePath.clear();
1923  mProjectScope.reset();
1924  mSaveVersion = fileVersion;
1925 
1926  // now get any properties
1927  profile.switchTask( tr( "Reading properties" ) );
1928  _getProperties( *doc, mProperties );
1929 
1930  // now get the data defined server properties
1931  mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
1932 
1933  QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1934 
1935 #if 0
1936  dump_( mProperties );
1937 #endif
1938 
1939  // get older style project title
1940  QString oldTitle;
1941  _getTitle( *doc, oldTitle );
1942 
1943  readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
1944 
1945  const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1946  if ( homePathNl.count() > 0 )
1947  {
1948  const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1949  const QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1950  if ( !homePath.isEmpty() )
1952  }
1953  else
1954  {
1955  emit homePathChanged();
1956  }
1957 
1958  const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1959  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1960  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1962  const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1963  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1964  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1965  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1967 
1968 
1969  const QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
1970  if ( !distanceUnitString.isEmpty() )
1971  setDistanceUnits( QgsUnitTypes::decodeDistanceUnit( distanceUnitString ) );
1972 
1973  const QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
1974  if ( !areaUnitString.isEmpty() )
1975  setAreaUnits( QgsUnitTypes::decodeAreaUnit( areaUnitString ) );
1976 
1977  QgsReadWriteContext context;
1978  context.setPathResolver( pathResolver() );
1979  context.setProjectTranslator( this );
1980 
1981  //crs
1982  QgsCoordinateReferenceSystem projectCrs;
1983  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1984  {
1985  // first preference - dedicated projectCrs node
1986  const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1987  if ( !srsNode.isNull() )
1988  {
1989  projectCrs.readXml( srsNode );
1990  }
1991 
1992  if ( !projectCrs.isValid() )
1993  {
1994  const QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1995  const long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1996  const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1997 
1998  // authid should be prioritized over all
1999  const bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
2000  if ( !authid.isEmpty() && !isUserAuthId )
2001  projectCrs = QgsCoordinateReferenceSystem( authid );
2002 
2003  // try the CRS
2004  if ( !projectCrs.isValid() && currentCRS >= 0 )
2005  {
2006  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
2007  }
2008 
2009  // if that didn't produce a match, try the proj.4 string
2010  if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
2011  {
2012  projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
2013  }
2014 
2015  // last just take the given id
2016  if ( !projectCrs.isValid() )
2017  {
2018  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
2019  }
2020  }
2021  }
2022  mCrs = projectCrs;
2023 
2024  QStringList datumErrors;
2025  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
2026  {
2027  emit missingDatumTransforms( datumErrors );
2028  }
2029  emit transformContextChanged();
2030 
2031  // map shading
2032  const QDomNode elevationShadingNode = doc->documentElement().namedItem( QStringLiteral( "elevation-shading-renderer" ) );
2033  if ( !elevationShadingNode.isNull() )
2034  {
2035  mElevationShadingRenderer.readXml( elevationShadingNode.toElement(), context );
2036  }
2038 
2039 
2040  //add variables defined in project file - do this early in the reading cycle, as other components
2041  //(e.g. layouts) may depend on these variables
2042  const QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
2043  const QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
2044 
2045  mCustomVariables.clear();
2046  if ( variableNames.length() == variableValues.length() )
2047  {
2048  for ( int i = 0; i < variableNames.length(); ++i )
2049  {
2050  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
2051  }
2052  }
2053  else
2054  {
2055  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
2056  }
2057 
2058  QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectMetadata" ) );
2059 
2060  if ( !element.isNull() )
2061  {
2062  mMetadata.readMetadataXml( element );
2063  }
2064  else
2065  {
2066  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
2067  mMetadata = QgsProjectMetadata();
2068  }
2069  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
2070  {
2071  // upgrade older title storage to storing within project metadata.
2072  mMetadata.setTitle( oldTitle );
2073  }
2074  emit metadataChanged();
2075 
2076  // Transaction mode
2077  element = doc->documentElement().firstChildElement( QStringLiteral( "transaction" ) );
2078  if ( !element.isNull() )
2079  {
2080  mTransactionMode = qgsEnumKeyToValue( element.attribute( QStringLiteral( "mode" ) ), Qgis::TransactionMode::Disabled );
2081  }
2082  else
2083  {
2084  // maybe older project => try read autotransaction
2085  element = doc->documentElement().firstChildElement( QStringLiteral( "autotransaction" ) );
2086  if ( ! element.isNull() )
2087  {
2088  mTransactionMode = static_cast<Qgis::TransactionMode>( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() );
2089  }
2090  }
2091 
2092  // read the layer tree from project file
2093  profile.switchTask( tr( "Loading layer tree" ) );
2094  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
2095 
2096  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2097  if ( !layerTreeElem.isNull() )
2098  {
2099  // Use a temporary tree to read the nodes to prevent signals being delivered to the models
2100  QgsLayerTree tempTree;
2101  tempTree.readChildrenFromXml( layerTreeElem, context );
2102  mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
2103  }
2104  else
2105  {
2106  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2107  }
2108 
2109  mLayerTreeRegistryBridge->setEnabled( false );
2110 
2111  // get the map layers
2112  profile.switchTask( tr( "Reading map layers" ) );
2113 
2114  loadProjectFlags( doc.get() );
2115 
2116  QList<QDomNode> brokenNodes;
2117  const bool clean = _getMapLayers( *doc, brokenNodes, flags );
2118 
2119  // review the integrity of the retrieved map layers
2120  if ( !clean && !( flags & Qgis::ProjectReadFlag::DontResolveLayers ) )
2121  {
2122  QgsDebugError( QStringLiteral( "Unable to get map layers from project file." ) );
2123 
2124  if ( !brokenNodes.isEmpty() )
2125  {
2126  QgsDebugError( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
2127  }
2128 
2129  // we let a custom handler decide what to do with missing layers
2130  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
2131  mBadLayerHandler->handleBadLayers( brokenNodes );
2132  }
2133 
2134  mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
2135  mMainAnnotationLayer->setTransformContext( mTransformContext );
2136 
2137  // load embedded groups and layers
2138  profile.switchTask( tr( "Loading embedded layers" ) );
2139  loadEmbeddedNodes( mRootGroup, flags );
2140 
2141  // Resolve references to other layers
2142  // Needs to be done here once all dependent layers are loaded
2143  profile.switchTask( tr( "Resolving layer references" ) );
2144  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2145  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
2146  {
2147  it.value()->resolveReferences( this );
2148  }
2149 
2150  mLayerTreeRegistryBridge->setEnabled( true );
2151 
2152  // now that layers are loaded, we can resolve layer tree's references to the layers
2153  profile.switchTask( tr( "Resolving references" ) );
2154  mRootGroup->resolveReferences( this );
2155 
2156  // we need to migrate old fashion designed QgsSymbolLayerReference to new ones
2157  if ( QgsProjectVersion( 3, 28, 0 ) > mSaveVersion )
2158  {
2162  }
2163 
2164  if ( !layerTreeElem.isNull() )
2165  {
2166  mRootGroup->readLayerOrderFromXml( layerTreeElem );
2167  }
2168 
2169  // Load pre 3.0 configuration
2170  const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
2171  if ( !layerTreeCanvasElem.isNull( ) )
2172  {
2173  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
2174  }
2175 
2176  // Convert pre 3.4 to create layers flags
2177  if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
2178  {
2179  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
2180  for ( const QString &layerId : requiredLayerIds )
2181  {
2182  if ( QgsMapLayer *layer = mapLayer( layerId ) )
2183  {
2184  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
2185  }
2186  }
2187  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
2188  for ( const QString &layerId : disabledLayerIds )
2189  {
2190  if ( QgsMapLayer *layer = mapLayer( layerId ) )
2191  {
2192  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
2193  }
2194  }
2195  }
2196 
2197  // Convert pre 3.26 default styles
2198  if ( QgsProjectVersion( 3, 26, 0 ) > mSaveVersion )
2199  {
2200  // Convert default symbols
2201  QString styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
2202  if ( !styleName.isEmpty() )
2203  {
2204  std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2206  }
2207  styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
2208  if ( !styleName.isEmpty() )
2209  {
2210  std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2212  }
2213  styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
2214  if ( !styleName.isEmpty() )
2215  {
2216  std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2218  }
2219  styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
2220  if ( !styleName.isEmpty() )
2221  {
2222  std::unique_ptr<QgsColorRamp> colorRamp( QgsStyle::defaultStyle()->colorRamp( styleName ) );
2223  styleSettings()->setDefaultColorRamp( colorRamp.get() );
2224  }
2225 
2226  // Convert randomize default symbol fill color
2227  styleSettings()->setRandomizeDefaultSymbolColor( readBoolEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ), true ) );
2228 
2229  // Convert default symbol opacity
2230  double opacity = 1.0;
2231  bool ok = false;
2232  // upgrade old setting
2233  double alpha = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ), 255, &ok );
2234  if ( ok )
2235  opacity = alpha / 255.0;
2236  double newOpacity = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ), 1.0, &ok );
2237  if ( ok )
2238  opacity = newOpacity;
2239  styleSettings()->setDefaultSymbolOpacity( opacity );
2240 
2241  // Cleanup
2242  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
2243  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
2244  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
2245  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
2246  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ) );
2247  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ) );
2248  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ) );
2249  }
2250 
2251  // After bad layer handling we might still have invalid layers,
2252  // store them in case the user wanted to handle them later
2253  // or wanted to pass them through when saving
2255  {
2256  profile.switchTask( tr( "Storing original layer properties" ) );
2257  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
2258  }
2259 
2260  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
2261 
2262  profile.switchTask( tr( "Loading map themes" ) );
2263  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
2265  mMapThemeCollection->readXml( *doc );
2266 
2267  profile.switchTask( tr( "Loading label settings" ) );
2268  mLabelingEngineSettings->readSettingsFromProject( this );
2270 
2271  profile.switchTask( tr( "Loading annotations" ) );
2272  mAnnotationManager->readXml( doc->documentElement(), context );
2274  {
2275  profile.switchTask( tr( "Loading layouts" ) );
2276  mLayoutManager->readXml( doc->documentElement(), *doc );
2277  }
2278 
2280  {
2281  profile.switchTask( tr( "Loading 3D Views" ) );
2282  m3DViewsManager->readXml( doc->documentElement(), *doc );
2283  }
2284 
2285  profile.switchTask( tr( "Loading bookmarks" ) );
2286  mBookmarkManager->readXml( doc->documentElement(), *doc );
2287 
2288  profile.switchTask( tr( "Loading sensors" ) );
2289  mSensorManager->readXml( doc->documentElement(), *doc );
2290 
2291  // reassign change dependencies now that all layers are loaded
2292  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2293  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
2294  {
2295  it.value()->setDependencies( it.value()->dependencies() );
2296  }
2297 
2298  profile.switchTask( tr( "Loading snapping settings" ) );
2299  mSnappingConfig.readProject( *doc );
2300  mAvoidIntersectionsMode = static_cast<Qgis::AvoidIntersectionsMode>( readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( Qgis::AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
2301 
2302  profile.switchTask( tr( "Loading view settings" ) );
2303  // restore older project scales settings
2304  mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
2305  const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
2306  QVector<double> res;
2307  for ( const QString &scale : scales )
2308  {
2309  const QStringList parts = scale.split( ':' );
2310  if ( parts.size() != 2 )
2311  continue;
2312 
2313  bool ok = false;
2314  const double denominator = QLocale().toDouble( parts[1], &ok );
2315  if ( ok )
2316  {
2317  res << denominator;
2318  }
2319  }
2320  mViewSettings->setMapScales( res );
2321  const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
2322  if ( !viewSettingsElement.isNull() )
2323  mViewSettings->readXml( viewSettingsElement, context );
2324 
2325  // restore style settings
2326  profile.switchTask( tr( "Loading style properties" ) );
2327  const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectStyleSettings" ) );
2328  if ( !styleSettingsElement.isNull() )
2329  {
2330  mStyleSettings->removeProjectStyle();
2331  mStyleSettings->readXml( styleSettingsElement, context, flags );
2332  }
2333 
2334  // restore time settings
2335  profile.switchTask( tr( "Loading temporal settings" ) );
2336  const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectTimeSettings" ) );
2337  if ( !timeSettingsElement.isNull() )
2338  mTimeSettings->readXml( timeSettingsElement, context );
2339 
2340 
2341  profile.switchTask( tr( "Loading elevation properties" ) );
2342  const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( QStringLiteral( "ElevationProperties" ) );
2343  if ( !elevationPropertiesElement.isNull() )
2344  mElevationProperties->readXml( elevationPropertiesElement, context );
2345  mElevationProperties->resolveReferences( this );
2346 
2347  profile.switchTask( tr( "Loading display settings" ) );
2348  {
2349  const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
2350  if ( !displaySettingsElement.isNull() )
2351  mDisplaySettings->readXml( displaySettingsElement, context );
2352  }
2353 
2354  profile.switchTask( tr( "Loading GPS settings" ) );
2355  {
2356  const QDomElement gpsSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectGpsSettings" ) );
2357  if ( !gpsSettingsElement.isNull() )
2358  mGpsSettings->readXml( gpsSettingsElement, context );
2359  mGpsSettings->resolveReferences( this );
2360  }
2361 
2362  profile.switchTask( tr( "Updating variables" ) );
2363  emit customVariablesChanged();
2364  profile.switchTask( tr( "Updating CRS" ) );
2365  emit crsChanged();
2366  emit ellipsoidChanged( ellipsoid() );
2367 
2368  // read the project: used by map canvas and legend
2369  profile.switchTask( tr( "Reading external settings" ) );
2370  emit readProject( *doc );
2371  emit readProjectWithContext( *doc, context );
2372 
2373  profile.switchTask( tr( "Updating interface" ) );
2374 
2375  snapSignalBlock.release();
2376  if ( !mBlockSnappingUpdates )
2377  emit snappingConfigChanged( mSnappingConfig );
2378 
2381  emit projectColorsChanged();
2382 
2383  // if all went well, we're allegedly in pristine state
2384  if ( clean )
2385  setDirty( false );
2386 
2387  QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUser ), 2 );
2388  QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUserFull ), 2 );
2389 
2393 
2394  if ( mTranslator )
2395  {
2396  //project possibly translated -> rename it with locale postfix
2397  const QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
2398  setFileName( newFileName );
2399 
2400  if ( write() )
2401  {
2402  setTitle( localeFileName );
2403  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Success );
2404  }
2405  else
2406  {
2407  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Critical );
2408  }
2409  }
2410 
2411  // lastly, make any previously editable layers editable
2412  const QMap<QString, QgsMapLayer *> loadedLayers = mapLayers();
2413  for ( auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2414  {
2415  if ( it.value()->isValid() && it.value()->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2416  {
2417  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2418  vl->startEditing();
2419  it.value()->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2420  }
2421  }
2422 
2423  return true;
2424 }
2425 
2426 bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
2427 {
2429 
2430  bool valid = true;
2431  const auto constChildren = group->children();
2432  for ( QgsLayerTreeNode *child : constChildren )
2433  {
2434  if ( QgsLayerTree::isGroup( child ) )
2435  {
2436  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
2437  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2438  {
2439  // make sure to convert the path from relative to absolute
2440  const QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
2441  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
2442  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
2443  if ( newGroup )
2444  {
2445  QList<QgsLayerTreeNode *> clonedChildren;
2446  const QList<QgsLayerTreeNode *> constChildren = newGroup->children();
2447  clonedChildren.reserve( constChildren.size() );
2448  for ( QgsLayerTreeNode *newGroupChild : constChildren )
2449  clonedChildren << newGroupChild->clone();
2450  delete newGroup;
2451 
2452  childGroup->insertChildNodes( 0, clonedChildren );
2453  }
2454  }
2455  else
2456  {
2457  loadEmbeddedNodes( childGroup, flags );
2458  }
2459  }
2460  else if ( QgsLayerTree::isLayer( child ) )
2461  {
2462  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2463  {
2464  QList<QDomNode> brokenNodes;
2465  if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
2466  {
2467  valid = valid && false;
2468  }
2469  }
2470  }
2471 
2472  }
2473 
2474  return valid;
2475 }
2476 
2477 QVariantMap QgsProject::customVariables() const
2478 {
2479  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
2481 
2482  return mCustomVariables;
2483 }
2484 
2485 void QgsProject::setCustomVariables( const QVariantMap &variables )
2486 {
2488 
2489  if ( variables == mCustomVariables )
2490  return;
2491 
2492  //write variable to project
2493  QStringList variableNames;
2494  QStringList variableValues;
2495 
2496  QVariantMap::const_iterator it = variables.constBegin();
2497  for ( ; it != variables.constEnd(); ++it )
2498  {
2499  variableNames << it.key();
2500  variableValues << it.value().toString();
2501  }
2502 
2503  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
2504  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
2505 
2506  mCustomVariables = variables;
2507  mProjectScope.reset();
2508 
2509  emit customVariablesChanged();
2510 }
2511 
2513 {
2515 
2516  *mLabelingEngineSettings = settings;
2518 }
2519 
2521 {
2523 
2524  return *mLabelingEngineSettings;
2525 }
2526 
2528 {
2530 
2531  mProjectScope.reset();
2532  return mLayerStore.get();
2533 }
2534 
2536 {
2538 
2539  return mLayerStore.get();
2540 }
2541 
2542 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
2543 {
2545 
2546  QList<QgsVectorLayer *> layers;
2547  const QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
2548  const auto constLayerIds = layerIds;
2549  for ( const QString &layerId : constLayerIds )
2550  {
2551  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
2552  layers << vlayer;
2553  }
2554  return layers;
2555 }
2556 
2557 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
2558 {
2560 
2561  QStringList list;
2562  list.reserve( layers.size() );
2563  for ( QgsVectorLayer *layer : layers )
2564  list << layer->id();
2565  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
2567 }
2568 
2570 {
2572 
2573  QgsExpressionContext context;
2574 
2577 
2578  return context;
2579 }
2580 
2582 {
2583  // this method is called quite extensively using QgsProject::instance()
2585 
2586  // MUCH cheaper to clone than build
2587  if ( mProjectScope )
2588  {
2589  std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2590 
2591  // we can't cache these variables
2592  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
2593  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
2594 
2595  // neither this function
2596  projectScope->addFunction( QStringLiteral( "sensor_data" ), new GetSensorData( sensorManager()->sensorsData() ) );
2597 
2598  return projectScope.release();
2599  }
2600 
2601  mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
2602 
2603  const QVariantMap vars = customVariables();
2604 
2605  QVariantMap::const_iterator it = vars.constBegin();
2606 
2607  for ( ; it != vars.constEnd(); ++it )
2608  {
2609  mProjectScope->setVariable( it.key(), it.value(), true );
2610  }
2611 
2612  QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
2613  if ( projectPath.isEmpty() )
2614  projectPath = mOriginalPath;
2615  const QString projectFolder = QFileInfo( projectPath ).path();
2616  const QString projectFilename = QFileInfo( projectPath ).fileName();
2617  const QString projectBasename = baseName();
2618 
2619  //add other known project variables
2620  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
2621  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
2622  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
2623  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
2624  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
2625  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
2626  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_last_saved" ), mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
2627  const QgsCoordinateReferenceSystem projectCrs = crs();
2628  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
2629  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
2630  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_description" ), projectCrs.description(), true, true ) );
2631  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
2632  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
2633  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_units" ), QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
2634  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_acronym" ), projectCrs.projectionAcronym(), true ) );
2635  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_ellipsoid" ), projectCrs.ellipsoidAcronym(), true ) );
2636  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_proj4" ), projectCrs.toProj(), true ) );
2637  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_wkt" ), projectCrs.toWkt( Qgis::CrsWktVariant::Preferred ), true ) );
2638 
2639  // metadata
2640  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
2641  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
2642  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
2643  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
2644 
2645  // keywords
2646  QVariantMap keywords;
2647  const QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2648  for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2649  {
2650  keywords.insert( it.key(), it.value() );
2651  }
2652  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
2653 
2654  // layers
2655  QVariantList layersIds;
2656  QVariantList layers;
2657  const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2658  layersIds.reserve( layersInProject.count() );
2659  layers.reserve( layersInProject.count() );
2660  for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2661  {
2662  layersIds << it.value()->id();
2663  layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( it.value() ) );
2664  }
2665  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_ids" ), layersIds, true ) );
2666  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
2667 
2668  mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
2669 
2671 }
2672 
2673 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
2674 {
2676 
2677  const QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2678 
2679  const auto constLayers = layers;
2680  for ( QgsMapLayer *layer : constLayers )
2681  {
2682  if ( ! layer->isValid() )
2683  return;
2684 
2685  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
2686  {
2687  vlayer->setReadExtentFromXml( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics );
2688  if ( vlayer->dataProvider() )
2689  vlayer->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues,
2691  }
2692 
2693  connect( layer, &QgsMapLayer::configChanged, this, [this] { setDirty(); } );
2694 
2695  // check if we have to update connections for layers with dependencies
2696  for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2697  {
2698  const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2699  if ( deps.contains( layer->id() ) )
2700  {
2701  // reconnect to change signals
2702  it.value()->setDependencies( deps );
2703  }
2704  }
2705  }
2706 
2707  updateTransactionGroups();
2708 
2709  if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
2710  emit snappingConfigChanged( mSnappingConfig );
2711 }
2712 
2713 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
2714 {
2716 
2717  if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
2718  emit snappingConfigChanged( mSnappingConfig );
2719 }
2720 
2721 void QgsProject::cleanTransactionGroups( bool force )
2722 {
2724 
2725  bool changed = false;
2726  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2727  {
2728  if ( tg.value()->isEmpty() || force )
2729  {
2730  delete tg.value();
2731  tg = mTransactionGroups.erase( tg );
2732  changed = true;
2733  }
2734  else
2735  {
2736  ++tg;
2737  }
2738  }
2739  if ( changed )
2740  emit transactionGroupsChanged();
2741 }
2742 
2743 void QgsProject::updateTransactionGroups()
2744 {
2746 
2747  mEditBufferGroup.clear();
2748 
2749  switch ( mTransactionMode )
2750  {
2752  {
2753  cleanTransactionGroups( true );
2754  return;
2755  }
2756  break;
2758  cleanTransactionGroups( true );
2759  break;
2761  cleanTransactionGroups( false );
2762  break;
2763  }
2764 
2765  bool tgChanged = false;
2766  const auto constLayers = mapLayers().values();
2767  for ( QgsMapLayer *layer : constLayers )
2768  {
2769  if ( ! layer->isValid() )
2770  continue;
2771 
2772  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2773  if ( ! vlayer )
2774  continue;
2775 
2776  switch ( mTransactionMode )
2777  {
2779  Q_ASSERT( false );
2780  break;
2782  {
2783  if ( QgsTransaction::supportsTransaction( vlayer ) )
2784  {
2785  const QString connString = QgsTransaction::connectionString( vlayer->source() );
2786  const QString key = vlayer->providerType();
2787 
2788  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
2789 
2790  if ( !tg )
2791  {
2792  tg = new QgsTransactionGroup();
2793  mTransactionGroups.insert( qMakePair( key, connString ), tg );
2794  tgChanged = true;
2795  }
2796  tg->addLayer( vlayer );
2797  }
2798  }
2799  break;
2801  {
2802  if ( vlayer->supportsEditing() )
2803  mEditBufferGroup.addLayer( vlayer );
2804  }
2805  break;
2806  }
2807  }
2808 
2809  if ( tgChanged )
2810  emit transactionGroupsChanged();
2811 }
2812 
2813 bool QgsProject::readLayer( const QDomNode &layerNode )
2814 {
2816 
2817  QgsReadWriteContext context;
2818  context.setPathResolver( pathResolver() );
2819  context.setProjectTranslator( this );
2820  context.setTransformContext( transformContext() );
2821  QList<QDomNode> brokenNodes;
2822  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
2823  {
2824  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
2825  // added layer for joins
2826  const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
2827  for ( QgsVectorLayer *layer : vectorLayers )
2828  {
2829  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
2830  layer->resolveReferences( this );
2831 
2832  if ( layer->isValid() && layer->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2833  {
2834  layer->startEditing();
2835  layer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2836  }
2837  }
2838  return true;
2839  }
2840  return false;
2841 }
2842 
2843 bool QgsProject::write( const QString &filename )
2844 {
2846 
2847  mFile.setFileName( filename );
2848  mCachedHomePath.clear();
2849  return write();
2850 }
2851 
2853 {
2855 
2856  mProjectScope.reset();
2857  if ( QgsProjectStorage *storage = projectStorage() )
2858  {
2859  QgsReadWriteContext context;
2860  // for projects stored in a custom storage, we have to check for the support
2861  // of relative paths since the storage most likely will not be in a file system
2862  const QString storageFilePath { storage->filePath( mFile.fileName() ) };
2863  if ( storageFilePath.isEmpty() )
2864  {
2866  }
2867  context.setPathResolver( pathResolver() );
2868 
2869  const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
2870  const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
2871 
2872  if ( !zip( tmpZipFilename ) )
2873  return false; // zip() already calls setError() when returning false
2874 
2875  QFile tmpZipFile( tmpZipFilename );
2876  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
2877  {
2878  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
2879  return false;
2880  }
2881 
2883  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
2884  {
2885  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
2886  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2887  if ( !messages.isEmpty() )
2888  err += QStringLiteral( "\n\n" ) + messages.last().message();
2889  setError( err );
2890  return false;
2891  }
2892 
2893  tmpZipFile.close();
2894  QFile::remove( tmpZipFilename );
2895 
2896  return true;
2897  }
2898 
2899  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2900  {
2901  return zip( mFile.fileName() );
2902  }
2903  else
2904  {
2905  // write project file even if the auxiliary storage is not correctly
2906  // saved
2907  const bool asOk = saveAuxiliaryStorage();
2908  const bool writeOk = writeProjectFile( mFile.fileName() );
2909  bool attachmentsOk = true;
2910  if ( !mArchive->files().isEmpty() )
2911  {
2912  const QFileInfo finfo( mFile.fileName() );
2913  const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
2914  attachmentsOk = mArchive->zip( attachmentsZip );
2915  }
2916 
2917  // errors raised during writing project file are more important
2918  if ( ( !asOk || !attachmentsOk ) && writeOk )
2919  {
2920  QStringList errorMessage;
2921  if ( !asOk )
2922  {
2923  const QString err = mAuxiliaryStorage->errorString();
2924  errorMessage.append( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2925  }
2926  if ( !attachmentsOk )
2927  {
2928  errorMessage.append( tr( "Unable to save attachments archive" ) );
2929  }
2930  setError( errorMessage.join( '\n' ) );
2931  }
2932 
2933  return asOk && writeOk && attachmentsOk;
2934  }
2935 }
2936 
2937 bool QgsProject::writeProjectFile( const QString &filename )
2938 {
2940 
2941  QFile projectFile( filename );
2942  clearError();
2943 
2944  // if we have problems creating or otherwise writing to the project file,
2945  // let's find out up front before we go through all the hand-waving
2946  // necessary to create all the Dom objects
2947  const QFileInfo myFileInfo( projectFile );
2948  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
2949  {
2950  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
2951  .arg( projectFile.fileName() ) );
2952  return false;
2953  }
2954 
2955  QgsReadWriteContext context;
2956  context.setPathResolver( pathResolver() );
2958 
2959  QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
2960 
2961  const QDomDocumentType documentType =
2962  QDomImplementation().createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
2963  QStringLiteral( "SYSTEM" ) );
2964  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
2965 
2966  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
2967  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
2968  qgisNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
2969 
2970  if ( !mSettings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
2971  {
2972  const QString newSaveUser = QgsApplication::userLoginName();
2973  const QString newSaveUserFull = QgsApplication::userFullName();
2974  qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2975  qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2976  mSaveUser = newSaveUser;
2977  mSaveUserFull = newSaveUserFull;
2978  mSaveDateTime = QDateTime::currentDateTime();
2979  qgisNode.setAttribute( QStringLiteral( "saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
2980  }
2981  else
2982  {
2983  mSaveUser.clear();
2984  mSaveUserFull.clear();
2985  mSaveDateTime = QDateTime();
2986  }
2987  doc->appendChild( qgisNode );
2988  mSaveVersion = QgsProjectVersion( Qgis::version() );
2989 
2990  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2991  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2992  qgisNode.appendChild( homePathNode );
2993 
2994  // title
2995  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2996  qgisNode.appendChild( titleNode );
2997 
2998  QDomElement transactionNode = doc->createElement( QStringLiteral( "transaction" ) );
2999  transactionNode.setAttribute( QStringLiteral( "mode" ), qgsEnumValueToKey( mTransactionMode ) );
3000  qgisNode.appendChild( transactionNode );
3001 
3002  QDomElement flagsNode = doc->createElement( QStringLiteral( "projectFlags" ) );
3003  flagsNode.setAttribute( QStringLiteral( "set" ), qgsFlagValueToKeys( mFlags ) );
3004  qgisNode.appendChild( flagsNode );
3005 
3006  const QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
3007  titleNode.appendChild( titleText );
3008 
3009  // write project CRS
3010  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
3011  mCrs.writeXml( srsNode, *doc );
3012  qgisNode.appendChild( srsNode );
3013 
3014  QDomElement elevationShadingNode = doc->createElement( QStringLiteral( "elevation-shading-renderer" ) );
3015  mElevationShadingRenderer.writeXml( elevationShadingNode, context );
3016  qgisNode.appendChild( elevationShadingNode );
3017 
3018  // write layer tree - make sure it is without embedded subgroups
3019  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
3021  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
3022 
3023  clonedRoot->writeXml( qgisNode, context );
3024  delete clonedRoot;
3025 
3026  mSnappingConfig.writeProject( *doc );
3027  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( mAvoidIntersectionsMode ) );
3028 
3029  // let map canvas and legend write their information
3030  emit writeProject( *doc );
3031 
3032  // within top level node save list of layers
3033  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3034 
3035  QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
3036  mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
3037  qgisNode.appendChild( annotationLayerNode );
3038 
3039  // Iterate over layers in zOrder
3040  // Call writeXml() on each
3041  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
3042 
3043  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
3044  while ( li != layers.end() )
3045  {
3046  QgsMapLayer *ml = li.value();
3047 
3048  if ( ml )
3049  {
3050  const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
3051  if ( emIt == mEmbeddedLayers.constEnd() )
3052  {
3053  QDomElement maplayerElem;
3054  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
3055  // not available, just write what we DO have
3056  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
3057  {
3058  // general layer metadata
3059  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
3060  ml->writeLayerXml( maplayerElem, *doc, context );
3061 
3063  maplayerElem.setAttribute( QStringLiteral( "editable" ), QStringLiteral( "1" ) );
3064  }
3065  else if ( ! ml->originalXmlProperties().isEmpty() )
3066  {
3067  QDomDocument document;
3068  if ( document.setContent( ml->originalXmlProperties() ) )
3069  {
3070  maplayerElem = document.firstChildElement();
3071  }
3072  else
3073  {
3074  QgsDebugError( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
3075  }
3076  }
3077 
3078  emit writeMapLayer( ml, maplayerElem, *doc );
3079 
3080  projectLayersNode.appendChild( maplayerElem );
3081  }
3082  else
3083  {
3084  // layer defined in an external project file
3085  // only save embedded layer if not managed by a legend group
3086  if ( emIt.value().second )
3087  {
3088  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
3089  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
3090  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
3091  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
3092  projectLayersNode.appendChild( mapLayerElem );
3093  }
3094  }
3095  }
3096  li++;
3097  }
3098 
3099  qgisNode.appendChild( projectLayersNode );
3100 
3101  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
3102  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
3103  for ( QgsMapLayer *layer : constCustomLayerOrder )
3104  {
3105  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
3106  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
3107  layerOrderNode.appendChild( mapLayerElem );
3108  }
3109  qgisNode.appendChild( layerOrderNode );
3110 
3111  mLabelingEngineSettings->writeSettingsToProject( this );
3112 
3113  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
3114  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
3115  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
3116 
3117  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
3118  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
3119  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
3120  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
3121 
3122  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( mDistanceUnits ) );
3123  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( mAreaUnits ) );
3124 
3125  // now add the optional extra properties
3126 #if 0
3127  dump_( mProperties );
3128 #endif
3129 
3130  QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 2 );
3131 
3132  if ( !mProperties.isEmpty() ) // only worry about properties if we
3133  // actually have any properties
3134  {
3135  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
3136  }
3137 
3138  QDomElement ddElem = doc->createElement( QStringLiteral( "dataDefinedServerProperties" ) );
3139  mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
3140  qgisNode.appendChild( ddElem );
3141 
3142  mMapThemeCollection->writeXml( *doc );
3143 
3144  mTransformContext.writeXml( qgisNode, context );
3145 
3146  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
3147  mMetadata.writeMetadataXml( metadataElem, *doc );
3148  qgisNode.appendChild( metadataElem );
3149 
3150  {
3151  const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
3152  qgisNode.appendChild( annotationsElem );
3153  }
3154 
3155  {
3156  const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
3157  qgisNode.appendChild( layoutElem );
3158  }
3159 
3160  {
3161  const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
3162  qgisNode.appendChild( views3DElem );
3163  }
3164 
3165  {
3166  const QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
3167  qgisNode.appendChild( bookmarkElem );
3168  }
3169 
3170  {
3171  const QDomElement sensorElem = mSensorManager->writeXml( *doc );
3172  qgisNode.appendChild( sensorElem );
3173  }
3174 
3175  {
3176  const QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
3177  qgisNode.appendChild( viewSettingsElem );
3178  }
3179 
3180  {
3181  const QDomElement styleSettingsElem = mStyleSettings->writeXml( *doc, context );
3182  qgisNode.appendChild( styleSettingsElem );
3183  }
3184 
3185  {
3186  const QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
3187  qgisNode.appendChild( timeSettingsElement );
3188  }
3189 
3190  {
3191  const QDomElement elevationPropertiesElement = mElevationProperties->writeXml( *doc, context );
3192  qgisNode.appendChild( elevationPropertiesElement );
3193  }
3194 
3195  {
3196  const QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
3197  qgisNode.appendChild( displaySettingsElem );
3198  }
3199 
3200  {
3201  const QDomElement gpsSettingsElem = mGpsSettings->writeXml( *doc, context );
3202  qgisNode.appendChild( gpsSettingsElem );
3203  }
3204 
3205  // now wrap it up and ship it to the project file
3206  doc->normalize(); // XXX I'm not entirely sure what this does
3207 
3208  // Create backup file
3209  if ( QFile::exists( fileName() ) )
3210  {
3211  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
3212  bool ok = true;
3213  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
3214  ok &= projectFile.open( QIODevice::ReadOnly );
3215 
3216  QByteArray ba;
3217  while ( ok && !projectFile.atEnd() )
3218  {
3219  ba = projectFile.read( 10240 );
3220  ok &= backupFile.write( ba ) == ba.size();
3221  }
3222 
3223  projectFile.close();
3224  backupFile.close();
3225 
3226  if ( !ok )
3227  {
3228  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
3229  return false;
3230  }
3231 
3232  const QFileInfo fi( fileName() );
3233  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
3234  utime( backupFile.fileName().toUtf8().constData(), &tb );
3235  }
3236 
3237  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3238  {
3239  projectFile.close(); // even though we got an error, let's make
3240  // sure it's closed anyway
3241 
3242  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
3243  return false;
3244  }
3245 
3246  QTemporaryFile tempFile;
3247  bool ok = tempFile.open();
3248  if ( ok )
3249  {
3250  QTextStream projectFileStream( &tempFile );
3251  doc->save( projectFileStream, 2 ); // save as utf-8
3252  ok &= projectFileStream.pos() > -1;
3253 
3254  ok &= tempFile.seek( 0 );
3255 
3256  QByteArray ba;
3257  while ( ok && !tempFile.atEnd() )
3258  {
3259  ba = tempFile.read( 10240 );
3260  ok &= projectFile.write( ba ) == ba.size();
3261  }
3262 
3263  ok &= projectFile.error() == QFile::NoError;
3264 
3265  projectFile.close();
3266  }
3267 
3268  tempFile.close();
3269 
3270  if ( !ok )
3271  {
3272  setError( tr( "Unable to save to file %1. Your project "
3273  "may be corrupted on disk. Try clearing some space on the volume and "
3274  "check file permissions before pressing save again." )
3275  .arg( projectFile.fileName() ) );
3276  return false;
3277  }
3278 
3279  setDirty( false ); // reset to pristine state
3280 
3281  emit projectSaved();
3282  return true;
3283 }
3284 
3285 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
3286 {
3288 
3289  bool propertiesModified;
3290  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3291 
3292  if ( propertiesModified )
3293  setDirty( true );
3294 
3295  return success;
3296 }
3297 
3298 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
3299 {
3301 
3302  bool propertiesModified;
3303  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3304 
3305  if ( propertiesModified )
3306  setDirty( true );
3307 
3308  return success;
3309 }
3310 
3311 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
3312 {
3314 
3315  bool propertiesModified;
3316  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3317 
3318  if ( propertiesModified )
3319  setDirty( true );
3320 
3321  return success;
3322 }
3323 
3324 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
3325 {
3327 
3328  bool propertiesModified;
3329  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3330 
3331  if ( propertiesModified )
3332  setDirty( true );
3333 
3334  return success;
3335 }
3336 
3337 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
3338 {
3340 
3341  bool propertiesModified;
3342  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3343 
3344  if ( propertiesModified )
3345  setDirty( true );
3346 
3347  return success;
3348 }
3349 
3350 QStringList QgsProject::readListEntry( const QString &scope,
3351  const QString &key,
3352  const QStringList &def,
3353  bool *ok ) const
3354 {
3355  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
3357 
3358  QgsProjectProperty *property = findKey_( scope, key, mProperties );
3359 
3360  QVariant value;
3361 
3362  if ( property )
3363  {
3364  value = property->value();
3365 
3366  const bool valid = QVariant::StringList == value.type();
3367  if ( ok )
3368  *ok = valid;
3369 
3370  if ( valid )
3371  {
3372  return value.toStringList();
3373  }
3374  }
3375  else if ( ok )
3376  *ok = false;
3377 
3378 
3379  return def;
3380 }
3381 
3382 QString QgsProject::readEntry( const QString &scope,
3383  const QString &key,
3384  const QString &def,
3385  bool *ok ) const
3386 {
3388 
3389  QgsProjectProperty *property = findKey_( scope, key, mProperties );
3390 
3391  QVariant value;
3392 
3393  if ( property )
3394  {
3395  value = property->value();
3396 
3397  const bool valid = value.canConvert( QVariant::String );
3398  if ( ok )
3399  *ok = valid;
3400 
3401  if ( valid )
3402  return value.toString();
3403  }
3404  else if ( ok )
3405  *ok = false;
3406 
3407  return def;
3408 }
3409 
3410 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
3411  bool *ok ) const
3412 {
3414 
3415  QgsProjectProperty *property = findKey_( scope, key, mProperties );
3416 
3417  QVariant value;
3418 
3419  if ( property )
3420  {
3421  value = property->value();
3422  }
3423 
3424  const bool valid = value.canConvert( QVariant::Int );
3425 
3426  if ( ok )
3427  {
3428  *ok = valid;
3429  }
3430 
3431  if ( valid )
3432  {
3433  return value.toInt();
3434  }
3435 
3436  return def;
3437 }
3438 
3439 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
3440  double def,
3441  bool *ok ) const
3442 {
3444 
3445  QgsProjectProperty *property = findKey_( scope, key, mProperties );
3446  if ( property )
3447  {
3448  const QVariant value = property->value();
3449 
3450  const bool valid = value.canConvert( QVariant::Double );
3451  if ( ok )
3452  *ok = valid;
3453 
3454  if ( valid )
3455  return value.toDouble();
3456  }
3457  else if ( ok )
3458  *ok = false;
3459 
3460  return def;
3461 }
3462 
3463 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
3464  bool *ok ) const
3465 {
3467 
3468  QgsProjectProperty *property = findKey_( scope, key, mProperties );
3469 
3470  if ( property )
3471  {
3472  const QVariant value = property->value();
3473 
3474  const bool valid = value.canConvert( QVariant::Bool );
3475  if ( ok )
3476  *ok = valid;
3477 
3478  if ( valid )
3479  return value.toBool();
3480  }
3481  else if ( ok )
3482  *ok = false;
3483 
3484  return def;
3485 }
3486 
3487 bool QgsProject::removeEntry( const QString &scope, const QString &key )
3488 {
3490 
3491  if ( findKey_( scope, key, mProperties ) )
3492  {
3493  removeKey_( scope, key, mProperties );
3494  setDirty( true );
3495  }
3496 
3497  return !findKey_( scope, key, mProperties );
3498 }
3499 
3500 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
3501 {
3503 
3504  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3505 
3506  QStringList entries;
3507 
3508  if ( foundProperty )
3509  {
3510  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3511 
3512  if ( propertyKey )
3513  { propertyKey->entryList( entries ); }
3514  }
3515 
3516  return entries;
3517 }
3518 
3519 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
3520 {
3522 
3523  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3524 
3525  QStringList entries;
3526 
3527  if ( foundProperty )
3528  {
3529  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3530 
3531  if ( propertyKey )
3532  { propertyKey->subkeyList( entries ); }
3533  }
3534 
3535  return entries;
3536 }
3537 
3539 {
3541 
3542  dump_( mProperties );
3543 }
3544 
3546 {
3548 
3549  QString filePath;
3550  switch ( filePathStorage() )
3551  {
3553  break;
3554 
3556  {
3557  // for projects stored in a custom storage, we need to ask to the
3558  // storage for the path, if the storage returns an empty path
3559  // relative paths are not supported
3560  if ( QgsProjectStorage *storage = projectStorage() )
3561  {
3562  filePath = storage->filePath( mFile.fileName() );
3563  }
3564  else
3565  {
3566  filePath = fileName();
3567  }
3568  break;
3569  }
3570  }
3571 
3572  return QgsPathResolver( filePath, mArchive->dir() );
3573 }
3574 
3575 QString QgsProject::readPath( const QString &src ) const
3576 {
3578 
3579  return pathResolver().readPath( src );
3580 }
3581 
3582 QString QgsProject::writePath( const QString &src ) const
3583 {
3585 
3586  return pathResolver().writePath( src );
3587 }
3588 
3589 void QgsProject::setError( const QString &errorMessage )
3590 {
3592 
3593  mErrorMessage = errorMessage;
3594 }
3595 
3596 QString QgsProject::error() const
3597 {
3599 
3600  return mErrorMessage;
3601 }
3602 
3603 void QgsProject::clearError()
3604 {
3606 
3607  setError( QString() );
3608 }
3609 
3611 {
3613 
3614  delete mBadLayerHandler;
3615  mBadLayerHandler = handler;
3616 }
3617 
3618 QString QgsProject::layerIsEmbedded( const QString &id ) const
3619 {
3621 
3622  const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
3623  if ( it == mEmbeddedLayers.constEnd() )
3624  {
3625  return QString();
3626  }
3627  return it.value().first;
3628 }
3629 
3630 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
3631  bool saveFlag, Qgis::ProjectReadFlags flags )
3632 {
3634 
3635  QgsDebugCall;
3636 
3637  static QString sPrevProjectFilePath;
3638  static QDateTime sPrevProjectFileTimestamp;
3639  static QDomDocument sProjectDocument;
3640 
3641  QString qgsProjectFile = projectFilePath;
3642  QgsProjectArchive archive;
3643  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3644  {
3645  archive.unzip( projectFilePath );
3646  qgsProjectFile = archive.projectFile();
3647  }
3648 
3649  const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
3650 
3651  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
3652  {
3653  sPrevProjectFilePath.clear();
3654 
3655  QFile projectFile( qgsProjectFile );
3656  if ( !projectFile.open( QIODevice::ReadOnly ) )
3657  {
3658  return false;
3659  }
3660 
3661  if ( !sProjectDocument.setContent( &projectFile ) )
3662  {
3663  return false;
3664  }
3665 
3666  sPrevProjectFilePath = projectFilePath;
3667  sPrevProjectFileTimestamp = projectFileTimestamp;
3668  }
3669 
3670  // does project store paths absolute or relative?
3671  bool useAbsolutePaths = true;
3672 
3673  const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
3674  if ( !propertiesElem.isNull() )
3675  {
3676  const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
3677  if ( !absElem.isNull() )
3678  {
3679  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
3680  }
3681  }
3682 
3683  QgsReadWriteContext embeddedContext;
3684  if ( !useAbsolutePaths )
3685  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
3686  embeddedContext.setProjectTranslator( this );
3687  embeddedContext.setTransformContext( transformContext() );
3688 
3689  const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
3690  if ( projectLayersElem.isNull() )
3691  {
3692  return false;
3693  }
3694 
3695  QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral( "maplayer" ) );
3696  while ( ! mapLayerElem.isNull() )
3697  {
3698  // get layer id
3699  const QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
3700  if ( id == layerId )
3701  {
3702  // layer can be embedded only once
3703  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
3704  {
3705  return false;
3706  }
3707 
3708  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
3709 
3710  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
3711  {
3712  return true;
3713  }
3714  else
3715  {
3716  mEmbeddedLayers.remove( layerId );
3717  return false;
3718  }
3719  }
3720  mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
3721  }
3722 
3723  return false;
3724 }
3725 
3726 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags )
3727 {
3729 
3730  QString qgsProjectFile = projectFilePath;
3731  QgsProjectArchive archive;
3732  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3733  {
3734  archive.unzip( projectFilePath );
3735  qgsProjectFile = archive.projectFile();
3736  }
3737 
3738  // open project file, get layer ids in group, add the layers
3739  QFile projectFile( qgsProjectFile );
3740  if ( !projectFile.open( QIODevice::ReadOnly ) )
3741  {
3742  return nullptr;
3743  }
3744 
3745  QDomDocument projectDocument;
3746  if ( !projectDocument.setContent( &projectFile ) )
3747  {
3748  return nullptr;
3749  }
3750 
3751  QgsReadWriteContext context;
3752  context.setPathResolver( pathResolver() );
3753  context.setProjectTranslator( this );
3755 
3757 
3758  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
3759  if ( !layerTreeElem.isNull() )
3760  {
3761  root->readChildrenFromXml( layerTreeElem, context );
3762  }
3763  else
3764  {
3765  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
3766  }
3767 
3768  QgsLayerTreeGroup *group = root->findGroup( groupName );
3769  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
3770  {
3771  // embedded groups cannot be embedded again
3772  delete root;
3773  return nullptr;
3774  }
3775 
3776  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
3777  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
3778  delete root;
3779  root = nullptr;
3780 
3781  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3782  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
3783 
3784  // set "embedded" to all children + load embedded layers
3785  mLayerTreeRegistryBridge->setEnabled( false );
3786  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
3787  mLayerTreeRegistryBridge->setEnabled( true );
3788 
3789  // consider the layers might be identify disabled in its project
3790  const auto constFindLayerIds = newGroup->findLayerIds();
3791  for ( const QString &layerId : constFindLayerIds )
3792  {
3793  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
3794  if ( layer )
3795  {
3796  layer->resolveReferences( this );
3797  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
3798  }
3799  }
3800 
3801  return newGroup;
3802 }
3803 
3804 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
3805 {
3807 
3808  const auto constChildren = group->children();
3809  for ( QgsLayerTreeNode *child : constChildren )
3810  {
3811  // all nodes in the subtree will have "embedded" custom property set
3812  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3813 
3814  if ( QgsLayerTree::isGroup( child ) )
3815  {
3816  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
3817  }
3818  else if ( QgsLayerTree::isLayer( child ) )
3819  {
3820  // load the layer into our project
3821  QList<QDomNode> brokenNodes;
3822  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
3823  }
3824  }
3825 }
3826 
3828 {
3830 
3832 }
3833 
3834 void QgsProject::setEvaluateDefaultValues( bool evaluateDefaultValues )
3835 {
3837 
3839 }
3840 
3842 {
3844 
3845  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
3847 }
3848 
3850 {
3852 
3853  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
3854 }
3855 
3857 {
3859 
3860  if ( mDistanceUnits == unit )
3861  return;
3862 
3863  mDistanceUnits = unit;
3864 
3865  emit distanceUnitsChanged();
3866 }
3867 
3869 {
3871 
3872  if ( mAreaUnits == unit )
3873  return;
3874 
3875  mAreaUnits = unit;
3876 
3877  emit areaUnitsChanged();
3878 }
3879 
3880 QString QgsProject::homePath() const
3881 {
3882  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
3884 
3885  if ( !mCachedHomePath.isEmpty() )
3886  return mCachedHomePath;
3887 
3888  const QFileInfo pfi( fileName() );
3889 
3890  if ( !mHomePath.isEmpty() )
3891  {
3892  const QFileInfo homeInfo( mHomePath );
3893  if ( !homeInfo.isRelative() )
3894  {
3895  mCachedHomePath = mHomePath;
3896  return mHomePath;
3897  }
3898  }
3899  else if ( !fileName().isEmpty() )
3900  {
3901 
3902  // If it's not stored in the file system, try to get the path from the storage
3903  if ( QgsProjectStorage *storage = projectStorage() )
3904  {
3905  const QString storagePath { storage->filePath( fileName() ) };
3906  if ( ! storagePath.isEmpty() && QFileInfo::exists( storagePath ) )
3907  {
3908  mCachedHomePath = QFileInfo( storagePath ).path();
3909  return mCachedHomePath;
3910  }
3911  }
3912 
3913  mCachedHomePath = pfi.path();
3914  return mCachedHomePath;
3915  }
3916 
3917  if ( !pfi.exists() )
3918  {
3919  mCachedHomePath = mHomePath;
3920  return mHomePath;
3921  }
3922 
3923  if ( !mHomePath.isEmpty() )
3924  {
3925  // path is relative to project file
3926  mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
3927  }
3928  else
3929  {
3930  mCachedHomePath = pfi.canonicalPath();
3931  }
3932  return mCachedHomePath;
3933 }
3934 
3936 {
3938 
3939  return mHomePath;
3940 }
3941 
3943 {
3944  // because relation aggregate functions are not thread safe
3946 
3947  return mRelationManager;
3948 }
3949 
3951 {
3953 
3954  return mLayoutManager.get();
3955 }
3956 
3958 {
3960 
3961  return mLayoutManager.get();
3962 }
3963 
3965 {
3967 
3968  return m3DViewsManager.get();
3969 }
3970 
3972 {
3974 
3975  return m3DViewsManager.get();
3976 }
3977 
3979 {
3981 
3982  return mBookmarkManager;
3983 }
3984 
3986 {
3988 
3989  return mBookmarkManager;
3990 }
3991 
3993 {
3995 
3996  return mSensorManager;
3997 }
3998 
4000 {
4002 
4003  return mSensorManager;
4004 }
4005 
4007 {
4009 
4010  return mViewSettings;
4011 }
4012 
4014 {
4016 
4017  return mViewSettings;
4018 }
4019 
4021 {
4023 
4024  return mStyleSettings;
4025 }
4026 
4028 {
4029  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
4031 
4032  return mStyleSettings;
4033 }
4034 
4036 {
4038 
4039  return mTimeSettings;
4040 }
4041 
4043 {
4045 
4046  return mTimeSettings;
4047 }
4048 
4050 {
4052 
4053  return mElevationProperties;
4054 }
4055 
4057 {
4059 
4060  return mElevationProperties;
4061 }
4062 
4064 {
4066 
4067  return mDisplaySettings;
4068 }
4069 
4071 {
4073 
4074  return mDisplaySettings;
4075 }
4076 
4078 {
4080 
4081  return mGpsSettings;
4082 }
4083 
4085 {
4087 
4088  return mGpsSettings;
4089 }
4090 
4092 {
4094 
4095  return mRootGroup;
4096 }
4097 
4099 {
4101 
4102  return mMapThemeCollection.get();
4103 }
4104 
4106 {
4108 
4109  return mAnnotationManager.get();
4110 }
4111 
4113 {
4115 
4116  return mAnnotationManager.get();
4117 }
4118 
4119 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
4120 {
4122 
4123  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4124  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4125  {
4126  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
4127  continue;
4128 
4129  if ( layers.contains( it.value() ) )
4130  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
4131  else
4132  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
4133  }
4134 
4138 }
4139 
4140 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
4141 {
4143 
4144  QList<QgsMapLayer *> nonIdentifiableLayers;
4145  nonIdentifiableLayers.reserve( layerIds.count() );
4146  for ( const QString &layerId : layerIds )
4147  {
4148  QgsMapLayer *layer = mapLayer( layerId );
4149  if ( layer )
4150  nonIdentifiableLayers << layer;
4151  }
4155 }
4156 
4158 {
4160 
4161  QStringList nonIdentifiableLayers;
4162 
4163  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4164  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4165  {
4166  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
4167  {
4168  nonIdentifiableLayers.append( it.value()->id() );
4169  }
4170  }
4171  return nonIdentifiableLayers;
4172 }
4173 
4175 {
4177 
4178  return mTransactionMode == Qgis::TransactionMode::AutomaticGroups;
4179 }
4180 
4181 void QgsProject::setAutoTransaction( bool autoTransaction )
4182 {
4184 
4185  if ( autoTransaction
4186  && mTransactionMode == Qgis::TransactionMode::AutomaticGroups )
4187  return;
4188 
4189  if ( ! autoTransaction
4190  && mTransactionMode == Qgis::TransactionMode::Disabled )
4191  return;
4192 
4193  if ( autoTransaction )
4194  mTransactionMode = Qgis::TransactionMode::AutomaticGroups;
4195  else
4196  mTransactionMode = Qgis::TransactionMode::Disabled;
4197 
4198  updateTransactionGroups();
4199 }
4200 
4202 {
4204 
4205  return mTransactionMode;
4206 }
4207 
4209 {
4211 
4212  if ( transactionMode == mTransactionMode )
4213  return true;
4214 
4215  // Check that all layer are not in edit mode
4216  const auto constLayers = mapLayers().values();
4217  for ( QgsMapLayer *layer : constLayers )
4218  {
4219  if ( layer->isEditable() )
4220  {
4221  QgsLogger::warning( tr( "Transaction mode can be changed only if all layers are not editable." ) );
4222  return false;
4223  }
4224  }
4225 
4226  mTransactionMode = transactionMode;
4227  updateTransactionGroups();
4228  return true;
4229 }
4230 
4231 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
4232 {
4234 
4235  return mTransactionGroups;
4236 }
4237 
4238 
4239 //
4240 // QgsMapLayerStore methods
4241 //
4242 
4243 
4245 {
4247 
4248  return mLayerStore->count();
4249 }
4250 
4252 {
4254 
4255  return mLayerStore->validCount();
4256 }
4257 
4258 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
4259 {
4260  // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
4262 
4263  return mLayerStore->mapLayer( layerId );
4264 }
4265 
4266 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
4267 {
4269 
4270  return mLayerStore->mapLayersByName( layerName );
4271 }
4272 
4273 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
4274 {
4276 
4277  QList<QgsMapLayer *> layers;
4278  const auto constMapLayers { mLayerStore->mapLayers() };
4279  for ( const auto &l : constMapLayers )
4280  {
4281  if ( ! l->shortName().isEmpty() )
4282  {
4283  if ( l->shortName() == shortName )
4284  layers << l;
4285  }
4286  else if ( l->name() == shortName )
4287  {
4288  layers << l;
4289  }
4290  }
4291  return layers;
4292 }
4293 
4294 bool QgsProject::unzip( const QString &filename, Qgis::ProjectReadFlags flags )
4295 {
4297 
4298  clearError();
4299  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
4300 
4301  // unzip the archive
4302  if ( !archive->unzip( filename ) )
4303  {
4304  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
4305  return false;
4306  }
4307 
4308  // test if zip provides a .qgs file
4309  if ( archive->projectFile().isEmpty() )
4310  {
4311  setError( tr( "Zip archive does not provide a project file" ) );
4312  return false;
4313  }
4314 
4315  // Keep the archive
4316  releaseHandlesToProjectArchive();
4317  mArchive = std::move( archive );
4318 
4319  // load auxiliary storage
4320  if ( !static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile().isEmpty() )
4321  {
4322  // database file is already a copy as it's been unzipped. So we don't open
4323  // auxiliary storage in copy mode in this case
4324  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile(), false ) );
4325  }
4326  else
4327  {
4328  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
4329  }
4330 
4331  // read the project file
4332  if ( ! readProjectFile( static_cast<QgsProjectArchive *>( mArchive.get() )->projectFile(), flags ) )
4333  {
4334  setError( tr( "Cannot read unzipped qgs project file" ) + QStringLiteral( ": " ) + error() );
4335  return false;
4336  }
4337 
4338  // Remove the temporary .qgs file
4339  static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
4340 
4341  return true;
4342 }
4343 
4344 bool QgsProject::zip( const QString &filename )
4345 {
4347 
4348  clearError();
4349 
4350  // save the current project in a temporary .qgs file
4351  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
4352  const QString baseName = QFileInfo( filename ).baseName();
4353  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
4354  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
4355 
4356  bool writeOk = false;
4357  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
4358  {
4359  writeOk = writeProjectFile( qgsFile.fileName() );
4360  qgsFile.close();
4361  }
4362 
4363  // stop here with an error message
4364  if ( ! writeOk )
4365  {
4366  setError( tr( "Unable to write temporary qgs file" ) );
4367  return false;
4368  }
4369 
4370  // save auxiliary storage
4371  const QFileInfo info( qgsFile );
4372  const QString asExt = QStringLiteral( ".%1" ).arg( QgsAuxiliaryStorage::extension() );
4373  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
4374 
4375  bool auxiliaryStorageSavedOk = true;
4376  if ( ! saveAuxiliaryStorage( asFileName ) )
4377  {
4378  const QString err = mAuxiliaryStorage->errorString();
4379  setError( tr( "Unable to save auxiliary storage file ('%1'). The project has been saved but the latest changes to auxiliary data cannot be recovered. It is recommended to reload the project." ).arg( err ) );
4380  auxiliaryStorageSavedOk = false;
4381 
4382  // fixes the current archive and keep the previous version of qgd
4383  if ( !mArchive->exists() )
4384  {
4385  releaseHandlesToProjectArchive();
4386  mArchive.reset( new QgsProjectArchive() );
4387  mArchive->unzip( mFile.fileName() );
4388  static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
4389 
4390  const QString auxiliaryStorageFile = static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile();
4391  if ( ! auxiliaryStorageFile.isEmpty() )
4392  {
4393  archive->addFile( auxiliaryStorageFile );
4394  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( auxiliaryStorageFile, false ) );
4395  }
4396  }
4397  }
4398  else
4399  {
4400  // in this case, an empty filename means that the auxiliary database is
4401  // empty, so we don't want to save it
4402  if ( QFile::exists( asFileName ) )
4403  {
4404  archive->addFile( asFileName );
4405  }
4406  }
4407 
4408  // create the archive
4409  archive->addFile( qgsFile.fileName() );
4410 
4411  // Add all other files
4412  const QStringList &files = mArchive->files();
4413  for ( const QString &file : files )
4414  {
4415  if ( !file.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
4416  {
4417  archive->addFile( file );
4418  }
4419  }
4420 
4421  // zip
4422  bool zipOk = true;
4423  if ( !archive->zip( filename ) )
4424  {
4425  setError( tr( "Unable to perform zip" ) );
4426  zipOk = false;
4427  }
4428 
4429  return auxiliaryStorageSavedOk && zipOk;
4430 }
4431 
4433 {
4435 
4436  return QgsZipUtils::isZipFile( mFile.fileName() );
4437 }
4438 
4439 QList<QgsMapLayer *> QgsProject::addMapLayers(
4440  const QList<QgsMapLayer *> &layers,
4441  bool addToLegend,
4442  bool takeOwnership )
4443 {
4445 
4446  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
4447  if ( !myResultList.isEmpty() )
4448  {
4449  // Update transform context
4450  for ( auto &l : myResultList )
4451  {
4452  l->setTransformContext( transformContext() );
4453  }
4454  if ( addToLegend )
4455  {
4456  emit legendLayersAdded( myResultList );
4457  }
4458  }
4459 
4460  if ( mAuxiliaryStorage )
4461  {
4462  for ( QgsMapLayer *mlayer : myResultList )
4463  {
4464  if ( mlayer->type() != Qgis::LayerType::Vector )
4465  continue;
4466 
4467  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
4468  if ( vl )
4469  {
4470  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
4471  }
4472  }
4473  }
4474 
4475  mProjectScope.reset();
4476 
4477  return myResultList;
4478 }
4479 
4480 QgsMapLayer *
4482  bool addToLegend,
4483  bool takeOwnership )
4484 {
4486 
4487  QList<QgsMapLayer *> addedLayers;
4488  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
4489  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
4490 }
4491 
4492 void QgsProject::removeAuxiliaryLayer( const QgsMapLayer *ml )
4493 {
4495 
4496  if ( ! ml || ml->type() != Qgis::LayerType::Vector )
4497  return;
4498 
4499  const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( ml );
4500  if ( vl && vl->auxiliaryLayer() )
4501  {
4502  const QgsDataSourceUri uri( vl->auxiliaryLayer()->source() );
4504  }
4505 }
4506 
4507 void QgsProject::removeMapLayers( const QStringList &layerIds )
4508 {
4510 
4511  for ( const auto &layerId : layerIds )
4512  removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4513 
4514  mProjectScope.reset();
4515  mLayerStore->removeMapLayers( layerIds );
4516 }
4517 
4518 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
4519 {
4521 
4522  for ( const auto &layer : layers )
4523  removeAuxiliaryLayer( layer );
4524 
4525  mProjectScope.reset();
4526  mLayerStore->removeMapLayers( layers );
4527 }
4528 
4529 void QgsProject::removeMapLayer( const QString &layerId )
4530 {
4532 
4533  removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4534  mProjectScope.reset();
4535  mLayerStore->removeMapLayer( layerId );
4536 }
4537 
4539 {
4541 
4542  removeAuxiliaryLayer( layer );
4543  mProjectScope.reset();
4544  mLayerStore->removeMapLayer( layer );
4545 }
4546 
4548 {
4550 
4551  mProjectScope.reset();
4552  return mLayerStore->takeMapLayer( layer );
4553 }
4554 
4556 {
4558 
4559  return mMainAnnotationLayer;
4560 }
4561 
4563 {
4565 
4566  if ( mLayerStore->count() == 0 )
4567  return;
4568 
4569  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
4570  mProjectScope.reset();
4571  mLayerStore->removeAllMapLayers();
4572 
4573  snapSingleBlocker.release();
4574  mSnappingConfig.clearIndividualLayerSettings();
4575  if ( !mBlockSnappingUpdates )
4576  emit snappingConfigChanged( mSnappingConfig );
4577 }
4578 
4580 {
4582 
4583  const QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
4584  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
4585  for ( ; it != layers.constEnd(); ++it )
4586  {
4587  it.value()->reload();
4588  }
4589 }
4590 
4591 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
4592 {
4593  // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
4595 
4596  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
4597 }
4598 
4599 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
4600 {
4602 
4603  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
4604 }
4605 
4607 {
4609 
4610  return &mEditBufferGroup;
4611 }
4612 
4614 {
4616 
4617  QgsCoordinateReferenceSystem defaultCrs;
4618 
4619  // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
4620  // 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)
4621  if ( mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
4622  || mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == QLatin1String( "2" ) )
4623  {
4624  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
4625  defaultCrs = crs();
4626  }
4627  else
4628  {
4629  // global crs
4630  const QString layerDefaultCrs = mSettings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
4631  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
4632  }
4633 
4634  return defaultCrs;
4635 }
4636 
4638 {
4640 
4642 }
4643 
4645 {
4647 
4649 }
4650 
4651 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
4652 {
4654 
4655  const QMap<QString, QgsMapLayer *> layers = mapLayers();
4656  bool empty = true;
4657  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4658  {
4659  if ( it.value()->type() != Qgis::LayerType::Vector )
4660  continue;
4661 
4662  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
4663  if ( vl && vl->auxiliaryLayer() )
4664  {
4665  vl->auxiliaryLayer()->save();
4666  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
4667  }
4668  }
4669 
4670  if ( !mAuxiliaryStorage->exists( *this ) && empty )
4671  {
4672  return true; // it's not an error
4673  }
4674  else if ( !filename.isEmpty() )
4675  {
4676  return mAuxiliaryStorage->saveAs( filename );
4677  }
4678  else
4679  {
4680  return mAuxiliaryStorage->saveAs( *this );
4681  }
4682 }
4683 
4684 QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
4685 {
4686  static QgsPropertiesDefinition sPropertyDefinitions
4687  {
4688  {
4690  QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
4691  },
4692  };
4693  return sPropertyDefinitions;
4694 }
4695 
4697 {
4698  mElevationShadingRenderer = elevationShadingRenderer;
4700 }
4701 
4703 {
4705 
4706  return mAuxiliaryStorage.get();
4707 }
4708 
4710 {
4712 
4713  return mAuxiliaryStorage.get();
4714 }
4715 
4716 QString QgsProject::createAttachedFile( const QString &nameTemplate )
4717 {
4719 
4720  const QDir archiveDir( mArchive->dir() );
4721  QTemporaryFile tmpFile( archiveDir.filePath( "XXXXXX_" + nameTemplate ), this );
4722  tmpFile.setAutoRemove( false );
4723  tmpFile.open();
4724  mArchive->addFile( tmpFile.fileName() );
4725  return tmpFile.fileName();
4726 }
4727 
4728 QStringList QgsProject::attachedFiles() const
4729 {
4731 
4732  QStringList attachments;
4733  const QString baseName = QFileInfo( fileName() ).baseName();
4734  const QStringList files = mArchive->files();
4735  attachments.reserve( files.size() );
4736  for ( const QString &file : files )
4737  {
4738  if ( QFileInfo( file ).baseName() != baseName )
4739  {
4740  attachments.append( file );
4741  }
4742  }
4743  return attachments;
4744 }
4745 
4746 bool QgsProject::removeAttachedFile( const QString &path )
4747 {
4749 
4750  return mArchive->removeFile( path );
4751 }
4752 
4753 QString QgsProject::attachmentIdentifier( const QString &attachedFile ) const
4754 {
4756 
4757  return QStringLiteral( "attachment:///%1" ).arg( QFileInfo( attachedFile ).fileName() );
4758 }
4759 
4760 QString QgsProject::resolveAttachmentIdentifier( const QString &identifier ) const
4761 {
4763 
4764  if ( identifier.startsWith( QLatin1String( "attachment:///" ) ) )
4765  {
4766  return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
4767  }
4768  return QString();
4769 }
4770 
4772 {
4773  // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
4775 
4776  return mMetadata;
4777 }
4778 
4780 {
4782 
4783  if ( metadata == mMetadata )
4784  return;
4785 
4786  mMetadata = metadata;
4787  mProjectScope.reset();
4788 
4789  emit metadataChanged();
4790 
4791  setDirty( true );
4792 }
4793 
4794 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
4795 {
4797 
4798  QSet<QgsMapLayer *> requiredLayers;
4799 
4800  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4801  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4802  {
4803  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4804  {
4805  requiredLayers.insert( it.value() );
4806  }
4807  }
4808  return requiredLayers;
4809 }
4810 
4811 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
4812 {
4814 
4815  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4816  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4817  {
4818  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4819  continue;
4820 
4821  if ( layers.contains( it.value() ) )
4822  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
4823  else
4824  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
4825  }
4826 }
4827 
4829 {
4831 
4832  // save colors to project
4833  QStringList customColors;
4834  QStringList customColorLabels;
4835 
4836  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
4837  for ( ; colorIt != colors.constEnd(); ++colorIt )
4838  {
4839  const QString color = QgsColorUtils::colorToString( ( *colorIt ).first );
4840  const QString label = ( *colorIt ).second;
4841  customColors.append( color );
4842  customColorLabels.append( label );
4843  }
4844  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
4845  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
4846  mProjectScope.reset();
4847  emit projectColorsChanged();
4848 }
4849 
4850 void QgsProject::setBackgroundColor( const QColor &color )
4851 {
4853 
4854  if ( mBackgroundColor == color )
4855  return;
4856 
4857  mBackgroundColor = color;
4858  emit backgroundColorChanged();
4859 }
4860 
4862 {
4864 
4865  return mBackgroundColor;
4866 }
4867 
4868 void QgsProject::setSelectionColor( const QColor &color )
4869 {
4871 
4872  if ( mSelectionColor == color )
4873  return;
4874 
4875  mSelectionColor = color;
4876  emit selectionColorChanged();
4877 }
4878 
4880 {
4882 
4883  return mSelectionColor;
4884 }
4885 
4886 void QgsProject::setMapScales( const QVector<double> &scales )
4887 {
4889 
4890  mViewSettings->setMapScales( scales );
4891 }
4892 
4893 QVector<double> QgsProject::mapScales() const
4894 {
4896 
4897  return mViewSettings->mapScales();
4898 }
4899 
4901 {
4903 
4904  mViewSettings->setUseProjectScales( enabled );
4905 }
4906 
4908 {
4910 
4911  return mViewSettings->useProjectScales();
4912 }
4913 
4914 void QgsProject::generateTsFile( const QString &locale )
4915 {
4917 
4918  QgsTranslationContext translationContext;
4919  translationContext.setProject( this );
4920  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
4921 
4922  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
4923 
4924  translationContext.writeTsFile( locale );
4925 }
4926 
4927 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
4928 {
4930 
4931  if ( !mTranslator )
4932  {
4933  return sourceText;
4934  }
4935 
4936  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
4937 
4938  if ( result.isEmpty() )
4939  {
4940  return sourceText;
4941  }
4942  return result;
4943 }
4944 
4946 {
4948 
4949  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
4950  if ( !layers.empty() )
4951  {
4952  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4953  {
4954  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
4955  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4956  {
4957  if ( !( ( *it )->accept( visitor ) ) )
4958  return false;
4959 
4960  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4961  return false;
4962  }
4963  }
4964  }
4965 
4966  if ( !mLayoutManager->accept( visitor ) )
4967  return false;
4968 
4969  if ( !mAnnotationManager->accept( visitor ) )
4970  return false;
4971 
4972  return true;
4973 }
4974 
4976 {
4977  return mElevationShadingRenderer;
4978 }
4979 
4980 void QgsProject::loadProjectFlags( const QDomDocument *doc )
4981 {
4983 
4984  QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectFlags" ) );
4986  if ( !element.isNull() )
4987  {
4988  flags = qgsFlagKeysToValue( element.attribute( QStringLiteral( "set" ) ), Qgis::ProjectFlags() );
4989  }
4990  else
4991  {
4992  // older project compatibility
4993  element = doc->documentElement().firstChildElement( QStringLiteral( "evaluateDefaultValues" ) );
4994  if ( !element.isNull() )
4995  {
4996  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
4998  }
4999 
5000  // Read trust layer metadata config in the project
5001  element = doc->documentElement().firstChildElement( QStringLiteral( "trust" ) );
5002  if ( !element.isNull() )
5003  {
5004  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
5006  }
5007  }
5008 
5009  setFlags( flags );
5010 }
5011 
5013 GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
5014  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
5015 {
5016  if ( !project )
5017  return;
5018 
5019  //build up color list from project. Do this in advance for speed
5020  QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
5021  const QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
5022 
5023  //generate list from custom colors
5024  int colorIndex = 0;
5025  for ( QStringList::iterator it = colorStrings.begin();
5026  it != colorStrings.end(); ++it )
5027  {
5028  const QColor color = QgsColorUtils::colorFromString( *it );
5029  QString label;
5030  if ( colorLabels.length() > colorIndex )
5031  {
5032  label = colorLabels.at( colorIndex );
5033  }
5034 
5035  mColors.insert( label.toLower(), color );
5036  colorIndex++;
5037  }
5038 }
5039 
5040 GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
5041  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
5042  , mColors( colors )
5043 {
5044 }
5045 
5046 QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5047 {
5048  const QString colorName = values.at( 0 ).toString().toLower();
5049  if ( mColors.contains( colorName ) )
5050  {
5051  return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
5052  }
5053  else
5054  return QVariant();
5055 }
5056 
5057 QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
5058 {
5059  return new GetNamedProjectColor( mColors );
5060 }
5061 
5062 // ----------------
5063 
5064 GetSensorData::GetSensorData( const QMap<QString, QgsAbstractSensor::SensorData> &sensorData )
5065  : QgsScopedExpressionFunction( QStringLiteral( "sensor_data" ),
5066  QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "expiration" ), true, 0 ),
5067  QStringLiteral( "Sensors" ) )
5068  , mSensorData( sensorData )
5069 {
5070 }
5071 
5072 QVariant GetSensorData::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5073 {
5074  const QString sensorName = values.at( 0 ).toString();
5075  const int expiration = values.at( 1 ).toInt();
5076  const qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
5077  if ( mSensorData.contains( sensorName ) )
5078  {
5079  if ( expiration <= 0 || ( timestamp - mSensorData[sensorName].lastTimestamp.toMSecsSinceEpoch() ) < expiration )
5080  {
5081  return mSensorData[sensorName].lastValue;
5082  }
5083  }
5084 
5085  return QVariant();
5086 }
5087 
5088 QgsScopedExpressionFunction *GetSensorData::clone() const
5089 {
5090  return new GetSensorData( mSensorData );
5091 }
@ DontLoad3DViews
Skip loading 3D views (since QGIS 3.26)
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
@ ForceReadOnlyLayers
Open layers in a read-only mode. (since QGIS 3.28)
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
@ DontResolveLayers
Don't resolve layer paths (i.e. don't load any layer content). Dramatically improves project read tim...
static QString version()
Version string.
Definition: qgis.cpp:258
QFlags< ProjectCapability > ProjectCapabilities
Flags which control project capabilities.
Definition: qgis.h:3550
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
Definition: qgis.h:3528
DistanceUnit
Units of distance.
Definition: qgis.h:4090
FilePathType
File path types.
Definition: qgis.h:1261
@ Relative
Relative path.
@ Absolute
Absolute path.
TransactionMode
Transaction mode.
Definition: qgis.h:3205
@ AutomaticGroups
Automatic transactional editing means that on supported datasources (postgres and geopackage database...
@ BufferedGroups
Buffered transactional editing means that all editable layers in the buffered transaction group are t...
@ Disabled
Edits are buffered locally and sent to the provider when toggling layer editing mode.
AreaUnit
Units of area.
Definition: qgis.h:4128
@ SquareMeters
Square meters.
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
Definition: qgis.h:3481
@ AvoidIntersectionsLayers
Overlap with features from a specified list of layers when digitizing new features not allowed.
@ AllowIntersections
Overlap with any feature allowed when digitizing new features.
ProjectFlag
Flags which control the behavior of QgsProjects.
Definition: qgis.h:3313
@ RememberLayerEditStatusBetweenSessions
If set, then any layers set to be editable will be stored in the project and immediately made editabl...
@ EvaluateDefaultValuesOnProviderSide
If set, default values for fields will be evaluated on the provider side when features from the proje...
@ TrustStoredLayerStatistics
If set, then layer statistics (such as the layer extent) will be read from values stored in the proje...
LayerType
Types of layers that can be added to a map.
Definition: qgis.h:114
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
QFlags< ProjectFlag > ProjectFlags
Definition: qgis.h:3320
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
virtual bool writeXml(QDomElement &collectionElem, const QgsPropertiesDefinition &definitions) const
Writes the current state of the property collection into an XML element.
Represents a map layer containing a set of georeferenced annotations, e.g.
void setTransformContext(const QgsCoordinateTransformContext &context) override
Sets the coordinate transform context to transformContext.
void reset()
Resets the annotation layer to a default state, and clears all items from it.
bool isEmpty() const
Returns true if the annotation layer is empty and contains no annotations.
Manages storage of a set of QgsAnnotation annotation objects.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
static const QgsSettingsEntryString * settingsLocaleUserLocale
Settings entry locale user locale.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ....
static QString userFullName()
Returns the user's operating system login account full display name.
static QString userLoginName()
Returns the user's operating system login account name.
Class allowing to manage the zip/unzip actions.
Definition: qgsarchive.h:35
void addFile(const QString &filename)
Add a new file to this archive.
Definition: qgsarchive.cpp:113
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
This is an abstract base class for any elements of a drag and drop form.
QString name() const
Returns the name of this element.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
bool save()
Commits changes and starts editing then.
Class providing some utility methods to manage auxiliary storage.
static QString extension()
Returns the extension used for auxiliary databases.
static bool deleteTable(const QgsDataSourceUri &uri)
Removes a table from the auxiliary storage.
Manages storage of a set of bookmarks.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all bookmarks present in the XML document.
void clear()
Removes and deletes all bookmarks from the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Q_GADGET Qgis::DistanceUnit mapUnits
QString toProj() const
Returns a Proj string representation of this CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
Contains information about the context in which a coordinate transform is executed.
void readSettings()
Reads the context's state from application settings.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context's state to a DOM element.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context's state from a DOM element.
Abstract base class for spatial data provider implementations.
@ ParallelThreadLoading
Skip credentials if the provided one are not valid, let the provider be invalid, avoiding to block th...
QFlags< ReadFlag > ReadFlags
@ EvaluateDefaultValues
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
Class for storing the component parts of a RDBMS data source URI (e.g.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
This class can render elevation shading on an image with different methods (eye dome lighting,...
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes configuration on a DOM element.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads configuration from a DOM element.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A abstract base class for defining QgsExpression functions.
An expression node for expression functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
Stores global configuration for labeling engine.
Class used to work with layer dependencies stored in a XML project or layer definition file.
Layer tree group node serves as a container for layers and further groups.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QList< QgsLayerTreeGroup * > findGroups(bool recursive=false) const
Find group layer nodes.
QString name() const override
Returns the group's name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already)
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the children, disconnect all the forwarded and external signals and sets their parent to null...
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.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
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.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Listens to the updates in map layer registry and does changes in layer tree.
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
static void storeOriginalLayersProperties(QgsLayerTreeGroup *group, const QDomDocument *doc)
Stores in a layer's originalXmlProperties the layer properties information.
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *project)
Updates an embedded group from a project.
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:50
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:70
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:41
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:60
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Manages storage of a set of layouts.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:131
static Qgis::LayerType typeFromString(const QString &string, bool &ok)
Returns the map layer type corresponding a string value.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the store.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the store.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the store.
void allLayersRemoved()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the store.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the store.
QgsMapLayer * mapLayer(const QString &id) const
Retrieve a pointer to a layer by layer id.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the store.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
QFlags< ReadFlag > ReadFlags
Definition: qgsmaplayer.h:645
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
void configChanged()
Emitted whenever the configuration is changed.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
Qgis::LayerType type
Definition: qgsmaplayer.h:82
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
virtual bool isEditable() const
Returns true if the layer can be edited.
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
bool isValid
Definition: qgsmaplayer.h:83
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:151
@ Removable
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
Definition: qgsmaplayer.h:152
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:641
@ FlagForceReadOnly
Force open as read only.
Definition: qgsmaplayer.h:643
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:640
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags(), QgsDataProvider *preloadedProvider=nullptr)
Sets state from DOM document.
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
static QgsDataProvider::ReadFlags providerReadFlags(const QDomNode &layerNode, QgsMapLayer::ReadFlags layerReadFlags)
Returns provider read flag deduced from layer read flags layerReadFlags and a dom node layerNode that...
Container class that allows storage of map themes consisting of visible map layers and layer styles.
Manages storage of a set of views.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Resolves relative paths into absolute paths and vice versa.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:120
QString projectFile() const
Returns the current .qgs project file or an empty string if there's none.
Definition: qgsarchive.cpp:140
QString auxiliaryStorageFile() const
Returns the current .qgd auxiliary storage file or an empty string if there's none.
Definition: qgsarchive.cpp:166
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Definition: qgsarchive.cpp:153
Interface for classes that handle missing layer files when reading project file.
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed.
Contains settings and properties relating to how a QgsProject should display values such as map coord...
void reset()
Resets the settings to a default state.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
Contains elevation properties for a QgsProject.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the property state from a DOM element.
void reset()
Resets the properties to a default state.
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Returns a DOM element representing the properties.
void resolveReferences(const QgsProject *project)
Resolves reference to layers from stored layer ID.
Class to convert from older project file versions to newer.
static Q_DECL_DEPRECATED void fixOldSymbolLayerReferences(const QMap< QString, QgsMapLayer * > &mapLayers)
QgsSymbolLayerReference uses QgsSymbolLayer unique uuid identifier since QGIS 3.30,...
Contains settings and properties relating to how a QgsProject should interact with a GPS device.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void reset()
Resets the settings to a default state.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
void resolveReferences(const QgsProject *project)
Resolves reference to layers from stored layer ID (if it has not been resolved already)
A structured metadata store for a map layer.
bool writeMetadataXml(QDomElement &metadataElement, QDomDocument &document) const override
Stores state in a DOM node.
void setCreationDateTime(const QDateTime &creationDateTime)
Sets the project's creation date/timestamp.
bool readMetadataXml(const QDomElement &metadataElement) override
Sets state from DOM document.
void setAuthor(const QString &author)
Sets the project author string.
Project property key node.
QString name() const
The name of the property is used as identifier.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
void removeKey(const QString &keyName)
Removes the specified key.
void dump(int tabs=0) const override
Dumps out the keys and values.
bool isEmpty() const
Returns true if this property contains no sub-keys.
virtual void clearKeys()
Deletes any sub-nodes from the property.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
QgsProjectProperty * find(const QString &propertyName) const
Attempts to find a property with a matching sub-key name.
void setName(const QString &name)
The name of the property is used as identifier.
QVariant value() const override
If this key has a value, it will be stored by its name in its properties.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
int count() const
Returns the number of sub-keys contained by this property.
QgsProjectPropertyValue * setValue(const QString &name, const QVariant &value)
Sets the value associated with this key.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
An Abstract Base Class for QGIS project property hierarchys.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
virtual bool isValue() const =0
Returns true if the property is a QgsProjectPropertyValue.
QgsProjectStorage * projectStorageFromUri(const QString &uri)
Returns storage implementation if the URI matches one. Returns nullptr otherwise (it is a normal file...
Metadata associated with a project.
Abstract interface for project storage - to be implemented by various backends and registered in QgsP...
Contains settings and properties relating to how a QgsProject should handle styling.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
void setDefaultSymbol(Qgis::SymbolType symbolType, QgsSymbol *symbol)
Sets the project default symbol for a given type.
void reset()
Resets the settings to a default state.
void removeProjectStyle()
Removes and deletes the project style database.
void setRandomizeDefaultSymbolColor(bool randomized)
Sets whether the default symbol fill color is randomized.
void setDefaultColorRamp(QgsColorRamp *colorRamp)
Sets the project default color ramp.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Reads the settings's state from a DOM element.
void setDefaultSymbolOpacity(double opacity)
Sets the default symbol opacity.
Contains temporal settings and properties for the project, this may be used when animating maps or sh...
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void reset()
Resets the settings to a default state.
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
A class to describe the version of a project.
QString text() const
Returns a string representation of the version.
int majorVersion() const
Returns the major version number.
Contains settings and properties relating to how a QgsProject should be displayed inside map canvas,...
bool useProjectScales() const
Returns true if project mapScales() are enabled.
void reset()
Resets the settings to a default state.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
QVector< double > mapScales() const
Returns the list of custom project map scales.
void mapScalesChanged()
Emitted when the list of custom project map scales changes.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
bool removeAttachedFile(const QString &path)
Removes the attached file.
QgsRelationManager * relationManager
Definition: qgsproject.h:117
bool write()
Writes the project to its current associated file (see fileName() ).
QgsProject(QObject *parent=nullptr, Qgis::ProjectCapabilities capabilities=Qgis::ProjectCapability::ProjectStyles)
Create a new QgsProject.
Definition: qgsproject.cpp:369
void removeMapLayer(const QString &layerId)
Remove a layer from the registry by layer ID.
Q_DECL_DEPRECATED bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
Qgis::DistanceUnit distanceUnits
Definition: qgsproject.h:124
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
void clear()
Clears the project, removing all settings and resetting it back to an empty, default state.
~QgsProject() override
Definition: qgsproject.cpp:459
QString error() const
Returns error message from previous read/write.
Q_DECL_DEPRECATED void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
Q_DECL_DEPRECATED void setNonIdentifiableLayers(const QList< QgsMapLayer * > &layers)
Set a list of layers which should not be taken into account on map identification.
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Qgis::ProjectFlags flags() const
Returns the project's flags, which dictate the behavior of the project.
Definition: qgsproject.h:207
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project's associated file.
Definition: qgsproject.cpp:838
QString presetHomePath() const
Returns any manual project home path setting, or an empty string if not set.
void setBackgroundColor(const QColor &color)
Sets the default background color used by default map canvases.
void setCrs(const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid=false)
Sets the project's native coordinate reference system.
Definition: qgsproject.cpp:944
QColor selectionColor
Definition: qgsproject.h:122
QString title() const
Returns the project's title.
Definition: qgsproject.cpp:506
bool commitChanges(QStringList &commitErrors, bool stopEditing=true, QgsVectorLayer *vectorLayer=nullptr)
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
Definition: qgsproject.cpp:742
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
Qgis::FilePathType filePathStorage() const
Returns the type of paths used when storing file paths in a QGS/QGZ project file.
Definition: qgsproject.cpp:913
QString createAttachedFile(const QString &nameTemplate)
Attaches a file to the project.
Q_DECL_DEPRECATED void mapScalesChanged()
Emitted when the list of custom project map scales changes.
void readVersionMismatchOccurred(const QString &fileVersion)
Emitted when a project is read and the version of QGIS used to save the project differs from the curr...
QString ellipsoid
Definition: qgsproject.h:114
void fileNameChanged()
Emitted when the file name of the project changes.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted when a layer is being saved.
const QgsSensorManager * sensorManager() const
Returns the project's sensor manager, which manages sensors within the project.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
void areaUnitsChanged()
Emitted when the default area units changes.
QgsPropertyCollection dataDefinedServerProperties() const
Returns the data defined properties used for overrides in user defined server parameters.
Definition: qgsproject.cpp:714
Q_DECL_DEPRECATED void nonIdentifiableLayersChanged(QStringList nonIdentifiableLayers)
Emitted when the list of layer which are excluded from map identification changes.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QString attachmentIdentifier(const QString &attachedFile) const
Returns an identifier for an attachment file path An attachment identifier is a string which does not...
QgsVectorLayerEditBufferGroup * editBufferGroup()
Returns the edit buffer group.
void setSelectionColor(const QColor &color)
Sets the color used to highlight selected features.
bool rollBack(QStringList &rollbackErrors, bool stopEditing=true, QgsVectorLayer *vectorLayer=nullptr)
Stops a current editing operation on vectorLayer and discards any uncommitted edits.
Definition: qgsproject.cpp:768
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
Q_DECL_DEPRECATED void setEvaluateDefaultValues(bool evaluateDefaultValues)
Defines if default values should be evaluated on provider side when requested and not when committed.
Qgis::AreaUnit areaUnits
Definition: qgsproject.h:125
void crsChanged()
Emitted when the CRS of the project has changed.
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.
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
QgsSnappingConfig snappingConfig
Definition: qgsproject.h:116
const QgsProjectGpsSettings * gpsSettings() const
Returns the project's GPS settings, which contains settings and properties relating to how a QgsProje...
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:794
void avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
void setDataDefinedServerProperties(const QgsPropertyCollection &properties)
Sets the data defined properties used for overrides in user defined server parameters to properties.
Definition: qgsproject.cpp:707
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
Definition: qgsproject.cpp:652
void distanceUnitsChanged()
Emitted when the default distance units changes.
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
const QgsBookmarkManager * bookmarkManager() const
Returns the project's bookmark manager, which manages bookmarks within the project.
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted after the basic initialization of a layer from the project file is done.
Q_DECL_DEPRECATED void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
bool startEditing(QgsVectorLayer *vectorLayer=nullptr)
Makes the layer editable.
Definition: qgsproject.cpp:721
void aboutToBeCleared()
Emitted when the project is about to be cleared.
Q_DECL_DEPRECATED void setTrustLayerMetadata(bool trust)
Sets the trust option allowing to indicate if the extent has to be read from the XML document when da...
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
Q_DECL_DEPRECATED void oldProjectVersionWarning(const QString &)
Emitted when an old project file is read.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets project's global labeling engine settings.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void metadataChanged()
Emitted when the project's metadata is changed.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:1181
QString resolveAttachmentIdentifier(const QString &identifier) const
Resolves an attachment identifier to a attachment file path.
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
QString absolutePath() const
Returns full absolute path to the project folder if the project is stored in a file system - derived ...
Definition: qgsproject.cpp:869
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
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...
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, bool saveFlag=true, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Creates a maplayer instance defined in an arbitrary project file.
QList< QgsVectorLayer * > avoidIntersectionsLayers
Definition: qgsproject.h:119
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString baseName() const
Returns the base name of the project file without the path and without extension - derived from fileN...
Definition: qgsproject.cpp:896
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:115
void generateTsFile(const QString &locale)
Triggers the collection strings of .qgs to be included in ts file and calls writeTsFile()
QStringList entryList(const QString &scope, const QString &key) const
Returns a list of child keys with values which exist within the the specified scope and key.
QgsAnnotationManager * annotationManager()
Returns pointer to the project's annotation manager.
QgsProjectDisplaySettings * displaySettings
Definition: qgsproject.h:126
Qgis::TransactionMode transactionMode() const
Returns the transaction mode.
QgsProjectMetadata metadata
Definition: qgsproject.h:120
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
QString saveUser() const
Returns the user name that did the last save.
Definition: qgsproject.cpp:564
void setProjectColors(const QgsNamedColorList &colors)
Sets the colors for the project's color scheme (see QgsProjectColorScheme).
bool setTransactionMode(Qgis::TransactionMode transactionMode)
Set transaction mode.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:113
void labelingEngineSettingsChanged()
Emitted when global configuration of the labeling engine changes.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Reads a boolean from the specified scope and key.
QgsMapLayerStore * layerStore()
Returns a pointer to the project's internal layer store.
void readProject(const QDomDocument &)
Emitted when a project is being read.
QString originalPath() const
Returns the original path associated with the project.
Definition: qgsproject.cpp:831
void setOriginalPath(const QString &path)
Sets the original path associated with the project.
Definition: qgsproject.cpp:824
void dumpProperties() const
Dump out current project properties to stderr.
QgsElevationShadingRenderer elevationShadingRenderer() const
Returns the elevation shading renderer used for map shading.
const QgsMapViewsManager * viewsManager() const
Returns the project's views manager, which manages map views (including 3d maps) in the project.
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
Definition: qgsproject.cpp:475
int validCount() const
Returns the number of registered valid layers.
const QgsLayoutManager * layoutManager() const
Returns the project's layout manager, which manages print layouts, atlases and reports within the pro...
void elevationShadingRendererChanged()
Emitted when the map shading renderer changes.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QString fileName
Definition: qgsproject.h:110
Q_DECL_DEPRECATED bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QStringList attachedFiles() const
Returns a map of all attached files with identifier and real paths.
void setMetadata(const QgsProjectMetadata &metadata)
Sets the project's metadata store.
void missingDatumTransforms(const QStringList &missingTransforms)
Emitted when datum transforms stored in the project are not available locally.
QgsTransactionGroup * transactionGroup(const QString &providerKey, const QString &connString)
Returns the matching transaction group from a provider key and connection string.
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:112
void readProjectWithContext(const QDomDocument &, QgsReadWriteContext &context)
Emitted when a project is being read.
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
QStringList nonIdentifiableLayers
Definition: qgsproject.h:109
void setAvoidIntersectionsMode(const Qgis::AvoidIntersectionsMode mode)
Sets the avoid intersections mode.
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed.
const QgsAuxiliaryStorage * auxiliaryStorage() const
Returns the current const auxiliary storage.
void reloadAllLayers()
Reload all registered layer's provider data caches, synchronising the layer with any changes in the d...
int count() const
Returns the number of registered layers.
void loadingLayerMessageReceived(const QString &layerName, const QList< QgsReadWriteContext::ReadWriteMessage > &messages)
Emitted when loading layers has produced some messages.
void setAreaUnits(Qgis::AreaUnit unit)
Sets the default area measurement units for the project.
void setTitle(const QString &title)
Sets the project's title.
Definition: qgsproject.cpp:492
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
void writeProject(QDomDocument &)
Emitted when the project is being written.
void setFlag(Qgis::ProjectFlag flag, bool enabled=true)
Sets whether a project flag is enabled.
Definition: qgsproject.cpp:552
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:853
bool readLayer(const QDomNode &layerNode)
Reads the layer described in the associated DOM node.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
Reads a double from the specified scope and key.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean value to the project file.
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:882
QDateTime lastSaveDateTime() const
Returns the date and time when the project was last saved.
Definition: qgsproject.cpp:578
void projectSaved()
Emitted when the project file has been written and closed.
Q_DECL_DEPRECATED bool trustLayerMetadata() const
Returns true if the trust option is activated, false otherwise.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void setEllipsoid(const QString &ellipsoid)
Sets the project's ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:978
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:998
QColor backgroundColor
Definition: qgsproject.h:121
void layerLoaded(int i, int n)
Emitted when a layer from a projects was read.
QStringList subkeyList(const QString &scope, const QString &key) const
Returns a list of child keys which contain other keys that exist within the the specified scope and k...
bool read(const QString &filename, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Reads given project file from the given file.
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Reads a string list from the specified scope and key.
void selectionColorChanged()
Emitted whenever the project's selection color has been changed.
bool topologicalEditing
Definition: qgsproject.h:123
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project's global labeling engine settings.
void removeAllMapLayers()
Removes all registered layers.
Q_DECL_DEPRECATED QVector< double > mapScales() const
Returns the list of custom project map scales.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:599
void backgroundColorChanged()
Emitted whenever the project's canvas background color has been changed.
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Create layer group instance defined in an arbitrary project file.
const QgsProjectViewSettings * viewSettings() const
Returns the project's view settings, which contains settings and properties relating to how a QgsProj...
QString readPath(const QString &filename) const
Transforms a filename read from the project file to an absolute path.
void registerTranslatableContainers(QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId)
Registers the containers that require translation into the translationContext.
Definition: qgsproject.cpp:632
void setElevationShadingRenderer(const QgsElevationShadingRenderer &elevationShadingRenderer)
Sets the elevation shading renderer used for global map shading.
void setFilePathStorage(Qgis::FilePathType type)
Sets the type of paths used when storing file paths in a QGS/QGZ project file.
Definition: qgsproject.cpp:921
Q_DECL_DEPRECATED QSet< QgsMapLayer * > requiredLayers() const
Returns a set of map layers that are required in the project and therefore they should not get remove...
void transformContextChanged()
Emitted when the project transformContext() is changed.
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
void legendLayersAdded(const QList< QgsMapLayer * > &layers)
Emitted, when a layer was added to the registry and the legend.
QVariantMap customVariables() const
A map of custom project variables.
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer * > &layers)
Sets the list of layers with which intersections should be avoided.
void homePathChanged()
Emitted when the home path of the project changes.
void dirtySet()
Emitted when setDirty(true) is called.
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const
Returns the default CRS for new layers based on the settings and the current project CRS.
QString saveUserFullName() const
Returns the full user name that did the last save.
Definition: qgsproject.cpp:571
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the registry.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
QString homePath
Definition: qgsproject.h:111
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:592
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
void isDirtyChanged(bool dirty)
Emitted when the project dirty status changes.
void setDistanceUnits(Qgis::DistanceUnit unit)
Sets the default distance measurement units for the project.
Q_DECL_DEPRECATED bool useProjectScales() const
Returns true if project mapScales() are enabled.
Q_DECL_DEPRECATED void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
void setPresetHomePath(const QString &path)
Sets the project's home path.
Definition: qgsproject.cpp:616
void setFlags(Qgis::ProjectFlags flags)
Sets the project's flags, which dictate the behavior of the project.
Definition: qgsproject.cpp:514
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName.
QgsProjectStorage * projectStorage() const
Returns pointer to project storage implementation that handles read/write of the project file.
Definition: qgsproject.cpp:845
QString layerIsEmbedded(const QString &id) const
Returns the source project file path if the layer with matching id is embedded from other project fil...
const QgsProjectTimeSettings * timeSettings() const
Returns the project's time settings, which contains the project's temporal range and other time based...
void topologicalEditingChanged()
Emitted when the topological editing flag has changed.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key from the specified scope.
QgsProjectVersion lastSaveVersion() const
Returns the QGIS version which the project was last saved using.
Definition: qgsproject.cpp:585
void avoidIntersectionsModeChanged()
Emitted whenever the avoid intersections mode has changed.
void loadingLayer(const QString &layerName)
Emitted when a layer is loaded.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Definition for a property.
Definition: qgsproperty.h:45
@ String
Any string value.
Definition: qgsproperty.h:59
Holds data provider key, description, and associated shared library file or function pointer informat...
virtual QgsProviderMetadata::ProviderCapabilities providerCapabilities() const
Returns the provider's capabilities.
@ ParallelCreateProvider
Indicates that the provider supports parallel creation, that is, can be created on another thread tha...
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString relativeToAbsoluteUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts relative path(s) to absolute path(s) in the given provider-specific URI.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
The class is used as a container of context for various read/write operations on other objects.
void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets data coordinate transform context to transformContext.
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
QList< QgsReadWriteContext::ReadWriteMessage > takeMessages()
Returns the stored messages and remove them.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
This class manages a set of relations between layers.
void clear()
Remove any relation managed by this class.
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
The QgsRunnableProviderCreator class is used when reading a project to create asynchronously provider...
void providerCreated(bool isValid, const QString &layerId)
Emitted when a provider is created with isValid set to True when the provider is valid.
QgsDataProvider * dataProvider()
Returns the created data provider.
void clear(const QString &group="startup")
clear Clear all profile data.
Expression function for use within a QgsExpressionContextScope.
Scoped object for logging of the runtime for a single operation or group of operations.
Manages sensors.
QDomElement writeXml(QDomDocument &document) const
Returns a DOM element representing the state of the manager.
void clear()
Deregisters and removes all sensors from the manager.
bool readXml(const QDomElement &element, const QDomDocument &document)
Reads the manager's state from a DOM element, restoring all sensors present in the XML document.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryInteger * settingsLayerParallelLoadingMaxCount
Settings entry maximum thread count used to load layer in parallel.
static const QgsSettingsEntryBool * settingsLayerParallelLoading
Settings entry whether layer are loading in parallel.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
This is a container for configuration of the snapping of the project.
bool addLayers(const QList< QgsMapLayer * > &layers)
Adds the specified layers as individual layers to the configuration with standard configuration.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
void reset()
reset to default values
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
void clearIndividualLayerSettings()
Removes all individual layer snapping settings.
bool removeLayers(const QList< QgsMapLayer * > &layers)
Removes the specified layers from the individual layer configuration.
An interface for classes which can visit style entity (e.g.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
void triggerIconRebuild()
Triggers emission of the rebuildIconPreviews() signal.
Definition: qgsstyle.cpp:3087
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition: qgsstyle.cpp:145
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
QString connectionString() const
Returns the connection string of the transaction.
Used for the collecting of strings from projects for translation and creation of ts files.
void registerTranslation(const QString &context, const QString &source)
Registers the source to be translated.
void setProject(QgsProject *project)
Sets the project being translated.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE Qgis::AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE Qgis::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
The edit buffer group manages a group of edit buffers.
bool commitChanges(QStringList &commitErrors, bool stopEditing=true)
Attempts to commit any changes to disk.
void clear()
Remove all layers from this edit buffer group.
bool rollBack(QStringList &rollbackErrors, bool stopEditing=true)
Stop editing and discard the edits.
void addLayer(QgsVectorLayer *layer)
Add a layer to this edit buffer group.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
bool loadAuxiliaryLayer(const QgsAuxiliaryStorage &storage, const QString &key=QString())
Loads the auxiliary layer for this vector layer.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
Q_INVOKABLE bool rollBack(bool deleteBuffer=true)
Stops a current editing operation and discards any uncommitted edits.
Q_INVOKABLE bool commitChanges(bool stopEditing=true)
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
QgsEditFormConfig editFormConfig
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
CORE_EXPORT bool isZipFile(const QString &filename)
Returns true if the file name is a zipped file ( i.e with a '.qgz' extension, false otherwise.
Definition: qgsziputils.cpp:32
CORE_EXPORT const QStringList files(const QString &zip)
Returns the list of files within a zip file.
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:5659
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition: qgis.h:5382
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:5741
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:5363
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition: qgis.h:5421
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given keys of a flag.
Definition: qgis.h:5443
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:5740
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:5653
#define QgsDebugCall
Definition: qgslogger.h:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2349
void _getProperties(const QDomDocument &doc, QgsProjectPropertyKey &project_properties)
Restores any optional properties found in "doc" to "properties".
QgsPropertyCollection getDataDefinedServerProperties(const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions)
Returns the data defined server properties collection found in "doc" to "dataDefinedServerProperties"...
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Returns the property that matches the given key sequence, if any.
Definition: qgsproject.cpp:146
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Removes a given key.
Definition: qgsproject.cpp:309
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value, bool &propertiesModified)
Adds the given key and value.
Definition: qgsproject.cpp:223
QgsProjectVersion getVersion(const QDomDocument &doc)
Returns the version string found in the given DOM document.
QStringList makeKeyTokens_(const QString &scope, const QString &key)
Takes the given scope and key and convert them to a string list of key tokens that will be used to na...
Definition: qgsproject.cpp:109
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
#define FONTMARKER_CHR_FIX
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
const QgsCoordinateReferenceSystem & crs
const QString & typeName
QgsDataProvider::ReadFlags flags
QDomElement layerElement
QString layerId
QgsDataProvider::ProviderOptions options
QString provider
QString dataSource
Setting options for loading annotation layers.
Setting options for creating vector data providers.
Single variable definition for use within a QgsExpressionContextScope.
Setting options for loading group layers.
Definition: qgsgrouplayer.h:52
Contains information relating to a node (i.e.