QGIS API Documentation  3.27.0-Master (e113457133)
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 "qgsrectangle.h"
39 #include "qgsrelationmanager.h"
40 #include "qgsannotationmanager.h"
41 #include "qgsvectorlayerjoininfo.h"
43 #include "qgsmapthemecollection.h"
44 #include "qgslayerdefinition.h"
45 #include "qgsunittypes.h"
46 #include "qgstransaction.h"
47 #include "qgstransactiongroup.h"
48 #include "qgsvectordataprovider.h"
50 #include "qgsmaplayerlistutils_p.h"
51 #include "qgsmeshlayer.h"
52 #include "qgslayoutmanager.h"
53 #include "qgsbookmarkmanager.h"
54 #include "qgsmaplayerstore.h"
55 #include "qgsziputils.h"
56 #include "qgsauxiliarystorage.h"
57 #include "qgssymbollayerutils.h"
58 #include "qgsapplication.h"
60 #include "qgsstyleentityvisitor.h"
61 #include "qgsprojectviewsettings.h"
64 #include "qgsprojecttimesettings.h"
65 #include "qgsvectortilelayer.h"
66 #include "qgsruntimeprofiler.h"
67 #include "qgsannotationlayer.h"
68 #include "qgspointcloudlayer.h"
70 #include "qgsgrouplayer.h"
71 #include "qgsmapviewsmanager.h"
73 #include "qgscombinedstylemodel.h"
74 
75 #include <algorithm>
76 #include <QApplication>
77 #include <QFileInfo>
78 #include <QDomNode>
79 #include <QObject>
80 #include <QTextStream>
81 #include <QTemporaryFile>
82 #include <QDir>
83 #include <QUrl>
84 #include <QStandardPaths>
85 #include <QUuid>
86 #include <QRegularExpression>
87 
88 #ifdef _MSC_VER
89 #include <sys/utime.h>
90 #else
91 #include <utime.h>
92 #endif
93 
94 // canonical project instance
95 QgsProject *QgsProject::sProject = nullptr;
96 
105 QStringList makeKeyTokens_( const QString &scope, const QString &key )
106 {
107  QStringList keyTokens = QStringList( scope );
108 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
109  keyTokens += key.split( '/', QString::SkipEmptyParts );
110 #else
111  keyTokens += key.split( '/', Qt::SkipEmptyParts );
112 #endif
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 )
370  : QObject( parent )
371  , mLayerStore( new QgsMapLayerStore( this ) )
372  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
373  , mSnappingConfig( this )
374  , mRelationManager( new QgsRelationManager( this ) )
375  , mAnnotationManager( new QgsAnnotationManager( this ) )
376  , mLayoutManager( new QgsLayoutManager( this ) )
377  , m3DViewsManager( new QgsMapViewsManager( this ) )
378  , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
379  , mViewSettings( new QgsProjectViewSettings( this ) )
380  , mStyleSettings( new QgsProjectStyleSettings( this ) )
381  , mTimeSettings( new QgsProjectTimeSettings( this ) )
382  , mElevationProperties( new QgsProjectElevationProperties( this ) )
383  , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
384  , mRootGroup( new QgsLayerTree )
385  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
386  , mArchive( new QgsArchive() )
387  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
388 {
389  mProperties.setName( QStringLiteral( "properties" ) );
390 
391  mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
392  mMainAnnotationLayer->setParent( this );
393 
394  clear();
395 
396  // bind the layer tree to the map layer registry.
397  // whenever layers are added to or removed from the registry,
398  // layer tree will be updated
399  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
400  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
401  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
402  connect( this, qOverload< const QList<QgsMapLayer *> & >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
403 
404  // proxy map layer store signals to this
405  connect( mLayerStore.get(), qOverload<const QStringList &>( &QgsMapLayerStore::layersWillBeRemoved ),
406  this, [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
407  connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ),
408  this, [ = ]( const QList<QgsMapLayer *> &layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
409  connect( mLayerStore.get(), qOverload< const QString & >( &QgsMapLayerStore::layerWillBeRemoved ),
410  this, [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
411  connect( mLayerStore.get(), qOverload< QgsMapLayer * >( &QgsMapLayerStore::layerWillBeRemoved ),
412  this, [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
413  connect( mLayerStore.get(), qOverload<const QStringList & >( &QgsMapLayerStore::layersRemoved ), this,
414  [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
415  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this,
416  [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
417  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this,
418  [ = ]() { mProjectScope.reset(); emit removeAll(); } );
419  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this,
420  [ = ]( const QList< QgsMapLayer * > &layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
421  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this,
422  [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
423 
424  if ( QgsApplication::instance() )
425  {
427  }
428 
429  connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ), this,
430  [ = ]( const QList<QgsMapLayer *> &layers )
431  {
432  for ( const auto &layer : layers )
433  {
434  disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
435  }
436  }
437  );
438  connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersAdded ), this,
439  [ = ]( const QList<QgsMapLayer *> &layers )
440  {
441  for ( const auto &layer : layers )
442  {
443  connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
444  }
445  }
446  );
447 
451 
452 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
453  mStyleSettings->combinedStyleModel()->addDefaultStyle();
454 #endif
455 }
456 
457 
459 {
460  mIsBeingDeleted = true;
461 
462  clear();
463  delete mBadLayerHandler;
464  delete mRelationManager;
465  delete mLayerTreeRegistryBridge;
466  delete mRootGroup;
467  if ( this == sProject )
468  {
469  sProject = nullptr;
470  }
471 }
472 
474 {
475  sProject = project;
476 }
477 
478 
480 {
481  if ( !sProject )
482  {
483  sProject = new QgsProject;
484 
486  }
487  return sProject;
488 }
489 
490 void QgsProject::setTitle( const QString &title )
491 {
492  if ( title == mMetadata.title() )
493  return;
494 
495  mMetadata.setTitle( title );
496  mProjectScope.reset();
497  emit metadataChanged();
498 
499  setDirty( true );
500 }
501 
502 QString QgsProject::title() const
503 {
504  return mMetadata.title();
505 }
506 
507 void QgsProject::setFlags( Qgis::ProjectFlags flags )
508 {
509  const bool oldEvaluateDefaultValues = mFlags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
510  const bool newEvaluateDefaultValues = flags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
511  if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
512  {
513  const QMap<QString, QgsMapLayer *> layers = mapLayers();
514  for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
515  {
516  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
517  {
518  vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, newEvaluateDefaultValues );
519  }
520  }
521  }
522 
523  const bool oldTrustLayerMetadata = mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
524  const bool newTrustLayerMetadata = flags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
525  if ( oldTrustLayerMetadata != newTrustLayerMetadata )
526  {
527  const QMap<QString, QgsMapLayer *> layers = mapLayers();
528  for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
529  {
530  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
531  {
532  vl->setReadExtentFromXml( newTrustLayerMetadata );
533  }
534  }
535  }
536 
537  mFlags = flags;
538 }
539 
540 void QgsProject::setFlag( Qgis::ProjectFlag flag, bool enabled )
541 {
542  Qgis::ProjectFlags newFlags = mFlags;
543  if ( enabled )
544  newFlags |= flag;
545  else
546  newFlags &= ~( static_cast< int >( flag ) );
547  setFlags( newFlags );
548 }
549 
550 QString QgsProject::saveUser() const
551 {
552  return mSaveUser;
553 }
554 
556 {
557  return mSaveUserFull;
558 }
559 
561 {
562  return mSaveDateTime;
563 }
564 
566 {
567  return mSaveVersion;
568 }
569 
571 {
572  return mDirty;
573 }
574 
575 void QgsProject::setDirty( const bool dirty )
576 {
577  if ( dirty && mDirtyBlockCount > 0 )
578  return;
579 
580  if ( dirty )
581  emit dirtySet();
582 
583  if ( mDirty == dirty )
584  return;
585 
586  mDirty = dirty;
587  emit isDirtyChanged( mDirty );
588 }
589 
590 void QgsProject::setPresetHomePath( const QString &path )
591 {
592  if ( path == mHomePath )
593  return;
594 
595  mHomePath = path;
596  mCachedHomePath.clear();
597  mProjectScope.reset();
598 
599  emit homePathChanged();
600 
601  setDirty( true );
602 }
603 
604 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
605 {
606  const QList<QgsAttributeEditorElement *> elements = parent->children();
607 
608  for ( QgsAttributeEditorElement *element : elements )
609  {
610  if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
611  {
612  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
613 
614  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
615 
616  if ( !container->children().empty() )
617  registerTranslatableContainers( translationContext, container, layerId );
618  }
619  }
620 }
621 
623 {
624  //register layers
625  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
626 
627  for ( const QgsLayerTreeLayer *layer : layers )
628  {
629  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
630 
631  QgsMapLayer *mapLayer = layer->layer();
633  {
634  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
635 
636  //register aliases and fields
637  const QgsFields fields = vlayer->fields();
638  for ( const QgsField &field : fields )
639  {
640  QString fieldName;
641  if ( field.alias().isEmpty() )
642  fieldName = field.name();
643  else
644  fieldName = field.alias();
645 
646  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
647 
648  if ( field.editorWidgetSetup().type() == QLatin1String( "ValueRelation" ) )
649  {
650  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
651  }
652  }
653 
654  //register formcontainers
655  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
656 
657  }
658  }
659 
660  //register layergroups
661  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
662  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
663  {
664  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
665  }
666 
667  //register relations
668  const QList<QgsRelation> &relations = mRelationManager->relations().values();
669  for ( const QgsRelation &relation : relations )
670  {
671  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
672  }
673 }
674 
676 {
677  mDataDefinedServerProperties = properties;
678 }
679 
681 {
682  return mDataDefinedServerProperties;
683 }
684 
686 {
687  switch ( mTransactionMode )
688  {
691  {
692  if ( ! vectorLayer )
693  return false;
694  return vectorLayer->startEditing();
695  }
696 
698  return mEditBufferGroup.startEditing();
699  }
700 
701  return false;
702 }
703 
704 bool QgsProject::commitChanges( QStringList &commitErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
705 {
706  switch ( mTransactionMode )
707  {
710  {
711  if ( ! vectorLayer )
712  {
713  commitErrors.append( tr( "Trying to commit changes without a layer specified. This only works if the transaction mode is buffered" ) );
714  return false;
715  }
716  bool success = vectorLayer->commitChanges( stopEditing );
717  commitErrors = vectorLayer->commitErrors();
718  return success;
719  }
720 
722  return mEditBufferGroup.commitChanges( commitErrors, stopEditing );
723  }
724 
725  return false;
726 }
727 
728 bool QgsProject::rollBack( QStringList &rollbackErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
729 {
730  switch ( mTransactionMode )
731  {
734  {
735  if ( ! vectorLayer )
736  {
737  rollbackErrors.append( tr( "Trying to roll back changes without a layer specified. This only works if the transaction mode is buffered" ) );
738  return false;
739  }
740  bool success = vectorLayer->rollBack( stopEditing );
741  rollbackErrors = vectorLayer->commitErrors();
742  return success;
743  }
744 
746  return mEditBufferGroup.rollBack( rollbackErrors, stopEditing );
747  }
748 
749  return false;
750 }
751 
752 void QgsProject::setFileName( const QString &name )
753 {
754  if ( name == mFile.fileName() )
755  return;
756 
757  const QString oldHomePath = homePath();
758 
759  mFile.setFileName( name );
760  mCachedHomePath.clear();
761  mProjectScope.reset();
762 
763  emit fileNameChanged();
764 
765  const QString newHomePath = homePath();
766  if ( newHomePath != oldHomePath )
767  emit homePathChanged();
768 
769  setDirty( true );
770 }
771 
772 QString QgsProject::fileName() const
773 {
774  return mFile.fileName();
775 }
776 
777 void QgsProject::setOriginalPath( const QString &path )
778 {
779  mOriginalPath = path;
780 }
781 
783 {
784  return mOriginalPath;
785 }
786 
787 QFileInfo QgsProject::fileInfo() const
788 {
789  return QFileInfo( mFile );
790 }
791 
793 {
795 }
796 
797 QDateTime QgsProject::lastModified() const
798 {
799  if ( QgsProjectStorage *storage = projectStorage() )
800  {
802  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
803  return metadata.lastModified;
804  }
805  else
806  {
807  return QFileInfo( mFile.fileName() ).lastModified();
808  }
809 }
810 
812 {
813  if ( projectStorage() )
814  return QString();
815 
816  if ( mFile.fileName().isEmpty() )
817  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
818 
819  return QFileInfo( mFile.fileName() ).absolutePath();
820 }
821 
823 {
824  if ( projectStorage() )
825  return QString();
826 
827  if ( mFile.fileName().isEmpty() )
828  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
829 
830  return QFileInfo( mFile.fileName() ).absoluteFilePath();
831 }
832 
833 QString QgsProject::baseName() const
834 {
835  if ( QgsProjectStorage *storage = projectStorage() )
836  {
838  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
839  return metadata.name;
840  }
841  else
842  {
843  return QFileInfo( mFile.fileName() ).completeBaseName();
844  }
845 }
846 
848 {
849  const bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
851 }
852 
854 {
855  switch ( type )
856  {
858  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
859  break;
861  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
862  break;
863  }
864 }
865 
867 {
868  return mCrs;
869 }
870 
871 void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
872 {
873  if ( crs != mCrs )
874  {
875  mCrs = crs;
876  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
877  mProjectScope.reset();
878 
879  // if annotation layer doesn't have a crs (i.e. in a newly created project), it should
880  // initially inherit the project CRS
881  if ( !mMainAnnotationLayer->crs().isValid() || mMainAnnotationLayer->isEmpty() )
882  mMainAnnotationLayer->setCrs( crs );
883 
884  setDirty( true );
885  emit crsChanged();
886  }
887 
888  if ( adjustEllipsoid )
890 }
891 
892 QString QgsProject::ellipsoid() const
893 {
894  if ( !crs().isValid() )
895  return geoNone();
896 
897  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), geoNone() );
898 }
899 
900 void QgsProject::setEllipsoid( const QString &ellipsoid )
901 {
902  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
903  return;
904 
905  mProjectScope.reset();
906  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
907  emit ellipsoidChanged( ellipsoid );
908 }
909 
911 {
912  return mTransformContext;
913 }
914 
916 {
917  if ( context == mTransformContext )
918  return;
919 
920  mTransformContext = context;
921  mProjectScope.reset();
922 
923  mMainAnnotationLayer->setTransformContext( context );
924  for ( auto &layer : mLayerStore.get()->mapLayers() )
925  {
926  layer->setTransformContext( context );
927  }
929 }
930 
932 {
933  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
934 
935  mProjectScope.reset();
936  mFile.setFileName( QString() );
937  mProperties.clearKeys();
938  mSaveUser.clear();
939  mSaveUserFull.clear();
940  mSaveDateTime = QDateTime();
941  mSaveVersion = QgsProjectVersion();
942  mHomePath.clear();
943  mCachedHomePath.clear();
944  mTransactionMode = Qgis::TransactionMode::Disabled;
945  mFlags = Qgis::ProjectFlags();
946  mDirty = false;
947  mCustomVariables.clear();
949  mMetadata = QgsProjectMetadata();
950  if ( !mSettings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
951  {
952  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
954  }
955  emit metadataChanged();
956 
958  context.readSettings();
959  setTransformContext( context );
960 
961  mEmbeddedLayers.clear();
962  mRelationManager->clear();
963  mAnnotationManager->clear();
964  mLayoutManager->clear();
965  m3DViewsManager->clear();
966  mBookmarkManager->clear();
967  mViewSettings->reset();
968  mTimeSettings->reset();
969  mElevationProperties->reset();
970  mDisplaySettings->reset();
971  mSnappingConfig.reset();
972  mAvoidIntersectionsMode = Qgis::AvoidIntersectionsMode::AllowIntersections;
975 
976  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
978 
979  mLabelingEngineSettings->clear();
980 
981  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
982  mArchive.reset( new QgsArchive() );
983 
984  // must happen after archive reset!
985  mStyleSettings->reset();
986 
988 
989  if ( !mIsBeingDeleted )
990  {
991  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
992  emit projectColorsChanged();
993  }
994 
995  // reset some default project properties
996  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
997  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
998  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
999 
1000  const bool defaultRelativePaths = mSettings.value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true ).toBool();
1002 
1003  //copy default units to project
1004  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
1005  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
1006 
1007  int red = mSettings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
1008  int green = mSettings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
1009  int blue = mSettings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
1010  setBackgroundColor( QColor( red, green, blue ) );
1011 
1012  red = mSettings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
1013  green = mSettings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
1014  blue = mSettings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
1015  const int alpha = mSettings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
1016  setSelectionColor( QColor( red, green, blue, alpha ) );
1017 
1018  mSnappingConfig.clearIndividualLayerSettings();
1019 
1021  mRootGroup->clear();
1022  if ( mMainAnnotationLayer )
1023  mMainAnnotationLayer->reset();
1024 
1025  snapSingleBlocker.release();
1026 
1027  if ( !mBlockSnappingUpdates )
1028  emit snappingConfigChanged( mSnappingConfig );
1029 
1030  setDirty( false );
1031  emit homePathChanged();
1032  emit cleared();
1033 }
1034 
1035 // basically a debugging tool to dump property list values
1036 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
1037 {
1038  QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
1039  topQgsPropertyKey.dump();
1040 }
1041 
1070 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
1071 {
1072  const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1073 
1074  if ( propertiesElem.isNull() ) // no properties found, so we're done
1075  {
1076  return;
1077  }
1078 
1079  const QDomNodeList scopes = propertiesElem.childNodes();
1080 
1081  if ( propertiesElem.firstChild().isNull() )
1082  {
1083  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
1084  return;
1085  }
1086 
1087  if ( ! project_properties.readXml( propertiesElem ) )
1088  {
1089  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
1090  }
1091 }
1092 
1099 QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
1100 {
1101  QgsPropertyCollection ddServerProperties;
1102  // Read data defined server properties
1103  const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral( "dataDefinedServerProperties" ) );
1104  if ( !ddElem.isNull() )
1105  {
1106  if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1107  {
1108  QgsDebugMsg( QStringLiteral( "dataDefinedServerProperties.readXml() failed" ) );
1109  }
1110  }
1111  return ddServerProperties;
1112 }
1113 
1118 static void _getTitle( const QDomDocument &doc, QString &title )
1119 {
1120  const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral( "title" ) );
1121 
1122  title.clear(); // by default the title will be empty
1123 
1124  if ( titleNode.isNull() )
1125  {
1126  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1127  return;
1128  }
1129 
1130  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1131  {
1132  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1133  return;
1134  }
1135 
1136  const QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1137 
1138  if ( !titleTextNode.isText() )
1139  {
1140  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1141  return;
1142  }
1143 
1144  const QDomText titleText = titleTextNode.toText();
1145 
1146  title = titleText.data();
1147 
1148 }
1149 
1150 static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1151 {
1152  const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1153 
1154  if ( !nl.count() )
1155  {
1156  QgsDebugMsg( QStringLiteral( "unable to find qgis element" ) );
1157  return;
1158  }
1159 
1160  const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1161 
1162  const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1163  lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
1164  lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
1165  lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral( "saveDateTime" ), QString() ), Qt::ISODate );
1166 }
1167 
1168 
1169 QgsProjectVersion getVersion( const QDomDocument &doc )
1170 {
1171  const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1172 
1173  if ( !nl.count() )
1174  {
1175  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
1176  return QgsProjectVersion( 0, 0, 0, QString() );
1177  }
1178 
1179  const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1180 
1181  const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1182  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
1183  return projectVersion;
1184 }
1185 
1186 
1188 {
1189  return mSnappingConfig;
1190 }
1191 
1193 {
1194  if ( mSnappingConfig == snappingConfig )
1195  return;
1196 
1197  mSnappingConfig = snappingConfig;
1198  setDirty( true );
1199  emit snappingConfigChanged( mSnappingConfig );
1200 }
1201 
1203 {
1204  if ( mAvoidIntersectionsMode == mode )
1205  return;
1206 
1207  mAvoidIntersectionsMode = mode;
1209 }
1210 
1211 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, Qgis::ProjectReadFlags flags )
1212 {
1213  // Layer order is set by the restoring the legend settings from project file.
1214  // This is done on the 'readProject( ... )' signal
1215 
1216  QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
1217 
1218  // process the map layer nodes
1219 
1220  if ( layerElement.isNull() ) // if we have no layers to process, bail
1221  {
1222  return true; // Decided to return "true" since it's
1223  // possible for there to be a project with no
1224  // layers; but also, more imporantly, this
1225  // would cause the tests/qgsproject to fail
1226  // since the test suite doesn't currently
1227  // support test layers
1228  }
1229 
1230  bool returnStatus = true;
1231  int numLayers = 0;
1232 
1233  while ( ! layerElement.isNull() )
1234  {
1235  numLayers++;
1236  layerElement = layerElement.nextSiblingElement( QStringLiteral( "maplayer" ) );
1237  }
1238 
1239  // order layers based on their dependencies
1240  QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), QStringLiteral( "projectload" ) );
1241  const QgsLayerDefinition::DependencySorter depSorter( doc );
1242  if ( depSorter.hasCycle() )
1243  return false;
1244 
1245  // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1246  if ( depSorter.hasMissingDependency() )
1247  returnStatus = false;
1248 
1249  emit layerLoaded( 0, numLayers );
1250 
1251  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1252  const int totalLayerCount = sortedLayerNodes.count();
1253 
1254  int i = 0;
1255  for ( const QDomNode &node : sortedLayerNodes )
1256  {
1257  const QDomElement element = node.toElement();
1258 
1259  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1260  if ( !name.isNull() )
1261  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1262 
1263  profile.switchTask( name );
1264 
1265  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1266  {
1267  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1268  }
1269  else
1270  {
1271  QgsReadWriteContext context;
1272  context.setPathResolver( pathResolver() );
1273  context.setProjectTranslator( this );
1275 
1276  if ( !addLayer( element, brokenNodes, context, flags ) )
1277  {
1278  returnStatus = false;
1279  }
1280  const auto messages = context.takeMessages();
1281  if ( !messages.isEmpty() )
1282  {
1283  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1284  }
1285  }
1286  emit layerLoaded( i + 1, totalLayerCount );
1287  i++;
1288  }
1289 
1290  return returnStatus;
1291 }
1292 
1293 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, Qgis::ProjectReadFlags flags )
1294 {
1295  const QString type = layerElem.attribute( QStringLiteral( "type" ) );
1296  QgsDebugMsgLevel( "Layer type is " + type, 4 );
1297  std::unique_ptr<QgsMapLayer> mapLayer;
1298 
1299  QgsScopedRuntimeProfile profile( tr( "Create layer" ), QStringLiteral( "projectload" ) );
1300 
1301  bool ok = false;
1302  const QgsMapLayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1303  if ( !ok )
1304  {
1305  QgsDebugMsg( QStringLiteral( "Unknown layer type \"%1\"" ).arg( type ) );
1306  return false;
1307  }
1308 
1309  switch ( layerType )
1310  {
1312  {
1313  mapLayer = std::make_unique<QgsVectorLayer>();
1314  // apply specific settings to vector layer
1315  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1316  {
1317  vl->setReadExtentFromXml( ( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( flags & Qgis::ProjectReadFlag::TrustLayerMetadata ) );
1318  }
1319  break;
1320  }
1321 
1323  mapLayer = std::make_unique<QgsRasterLayer>();
1324  break;
1325 
1327  mapLayer = std::make_unique<QgsMeshLayer>();
1328  break;
1329 
1331  mapLayer = std::make_unique<QgsVectorTileLayer>();
1332  break;
1333 
1335  mapLayer = std::make_unique<QgsPointCloudLayer>();
1336  break;
1337 
1339  {
1340  const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1341  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1342  break;
1343  }
1344 
1346  {
1347  const QgsAnnotationLayer::LayerOptions options( mTransformContext );
1348  mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1349  break;
1350  }
1351 
1353  {
1354  const QgsGroupLayer::LayerOptions options( mTransformContext );
1355  mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1356  break;
1357  }
1358  }
1359 
1360  if ( !mapLayer )
1361  {
1362  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1363  return false;
1364  }
1365 
1366  Q_CHECK_PTR( mapLayer ); // NOLINT
1367 
1368  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1369  // because if it was, the newly created layer will not be added to the store and it would leak.
1370  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1371  Q_ASSERT( ! layerId.isEmpty() );
1372  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1373 
1374  // have the layer restore state that is stored in Dom node
1375  QgsMapLayer::ReadFlags layerFlags = QgsMapLayer::ReadFlags();
1376  if ( flags & Qgis::ProjectReadFlag::DontResolveLayers )
1377  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1378  // Propagate trust layer metadata flag
1379  if ( ( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( flags & Qgis::ProjectReadFlag::TrustLayerMetadata ) )
1381 
1382  profile.switchTask( tr( "Load layer source" ) );
1383  const bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1384 
1385  profile.switchTask( tr( "Add layer to project" ) );
1386  QList<QgsMapLayer *> newLayers;
1387  newLayers << mapLayer.get();
1388  if ( layerIsValid || flags & Qgis::ProjectReadFlag::DontResolveLayers )
1389  {
1390  emit readMapLayer( mapLayer.get(), layerElem );
1391  addMapLayers( newLayers );
1392  // Try to resolve references here (this is necessary to set up joined fields that will be possibly used by
1393  // virtual layers that point to this layer's joined field in their query otherwise they won't be valid ),
1394  // a second attempt to resolve references will be done after all layers are loaded
1395  // see https://github.com/qgis/QGIS/issues/46834
1396  if ( QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1397  {
1398  vLayer->joinBuffer()->resolveReferences( this );
1399  }
1400  }
1401  else
1402  {
1403  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1404  addMapLayers( newLayers, false );
1405  newLayers.first();
1406  QgsDebugMsg( "Unable to load " + type + " layer" );
1407  brokenNodes.push_back( layerElem );
1408  }
1409 
1410  const bool wasEditable = layerElem.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
1411  if ( wasEditable )
1412  {
1413  mapLayer->setCustomProperty( QStringLiteral( "_layer_was_editable" ), true );
1414  }
1415  else
1416  {
1417  mapLayer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
1418  }
1419 
1420  // It should be safe to delete the layer now if layer was stored, because all the store
1421  // had to to was to reset the data source in case the validity changed.
1422  if ( ! layerWasStored )
1423  {
1424  mapLayer.release();
1425  }
1426 
1427  return layerIsValid;
1428 }
1429 
1430 bool QgsProject::read( const QString &filename, Qgis::ProjectReadFlags flags )
1431 {
1432  mFile.setFileName( filename );
1433  mCachedHomePath.clear();
1434  mProjectScope.reset();
1435 
1436  return read( flags );
1437 }
1438 
1439 bool QgsProject::read( Qgis::ProjectReadFlags flags )
1440 {
1441  const QString filename = mFile.fileName();
1442  bool returnValue;
1443 
1444  if ( QgsProjectStorage *storage = projectStorage() )
1445  {
1446  QTemporaryFile inDevice;
1447  if ( !inDevice.open() )
1448  {
1449  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1450  return false;
1451  }
1452 
1453  QgsReadWriteContext context;
1454  context.setProjectTranslator( this );
1455  if ( !storage->readProject( filename, &inDevice, context ) )
1456  {
1457  QString err = tr( "Unable to open %1" ).arg( filename );
1458  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1459  if ( !messages.isEmpty() )
1460  err += QStringLiteral( "\n\n" ) + messages.last().message();
1461  setError( err );
1462  return false;
1463  }
1464  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1465  }
1466  else
1467  {
1468  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1469  {
1470  returnValue = unzip( mFile.fileName(), flags );
1471  }
1472  else
1473  {
1474  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1475  const QFileInfo finfo( mFile.fileName() );
1476  const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1477  if ( QFile( attachmentsZip ).exists() )
1478  {
1479  std::unique_ptr<QgsArchive> archive( new QgsArchive() );
1480  if ( archive->unzip( attachmentsZip ) )
1481  {
1482  mArchive = std::move( archive );
1483  }
1484  }
1485  returnValue = readProjectFile( mFile.fileName(), flags );
1486  }
1487 
1488  //on translation we should not change the filename back
1489  if ( !mTranslator )
1490  {
1491  mFile.setFileName( filename );
1492  mCachedHomePath.clear();
1493  mProjectScope.reset();
1494  }
1495  else
1496  {
1497  //but delete the translator
1498  mTranslator.reset( nullptr );
1499  }
1500  }
1501  emit homePathChanged();
1502  return returnValue;
1503 }
1504 
1505 bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlags flags )
1506 {
1507  // avoid multiple emission of snapping updated signals
1508  ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
1509 
1510  QFile projectFile( filename );
1511  clearError();
1512 
1513  QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
1514  QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), QStringLiteral( "projectload" ) );
1515 
1516  const QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), QgsApplication::settingsLocaleUserLocale.value() );
1517 
1518  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1519  {
1520  mTranslator.reset( new QTranslator() );
1521  ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1522  }
1523 
1524  profile.switchTask( tr( "Reading project file" ) );
1525  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1526 
1527  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1528  {
1529  projectFile.close();
1530 
1531  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1532 
1533  return false;
1534  }
1535 
1536  // location of problem associated with errorMsg
1537  int line, column;
1538  QString errorMsg;
1539 
1540  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1541  {
1542  const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1543  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1544 
1545  QgsDebugMsg( errorString );
1546 
1547  projectFile.close();
1548 
1549  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1550 
1551  return false;
1552  }
1553 
1554  projectFile.close();
1555 
1556  QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1557 
1558  // get project version string, if any
1559  const QgsProjectVersion fileVersion = getVersion( *doc );
1560  const QgsProjectVersion thisVersion( Qgis::version() );
1561 
1562  profile.switchTask( tr( "Updating project file" ) );
1563  if ( thisVersion > fileVersion )
1564  {
1565  const bool isOlderMajorVersion = fileVersion.majorVersion() < thisVersion.majorVersion();
1566 
1567  if ( isOlderMajorVersion )
1568  {
1569  QgsLogger::warning( "Loading a file that was saved with an older "
1570  "version of qgis (saved in " + fileVersion.text() +
1571  ", loaded in " + Qgis::version() +
1572  "). Problems may occur." );
1573  }
1574 
1575  QgsProjectFileTransform projectFile( *doc, fileVersion );
1576 
1577  // Shows a warning when an old project file is read.
1579  emit oldProjectVersionWarning( fileVersion.text() );
1581  emit readVersionMismatchOccurred( fileVersion.text() );
1582 
1583  projectFile.updateRevision( thisVersion );
1584  }
1585  else if ( fileVersion > thisVersion )
1586  {
1587  QgsLogger::warning( "Loading a file that was saved with a newer "
1588  "version of qgis (saved in " + fileVersion.text() +
1589  ", loaded in " + Qgis::version() +
1590  "). Problems may occur." );
1591 
1592  emit readVersionMismatchOccurred( fileVersion.text() );
1593  }
1594 
1595  // start new project, just keep the file name and auxiliary storage
1596  profile.switchTask( tr( "Creating auxiliary storage" ) );
1597  const QString fileName = mFile.fileName();
1598  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1599  std::unique_ptr<QgsArchive> archive = std::move( mArchive );
1600  clear();
1601  mAuxiliaryStorage = std::move( aStorage );
1602  mArchive = std::move( archive );
1603  mFile.setFileName( fileName );
1604  mCachedHomePath.clear();
1605  mProjectScope.reset();
1606  mSaveVersion = fileVersion;
1607 
1608  // now get any properties
1609  profile.switchTask( tr( "Reading properties" ) );
1610  _getProperties( *doc, mProperties );
1611 
1612  // now get the data defined server properties
1613  mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
1614 
1615  QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1616 
1617 #if 0
1618  dump_( mProperties );
1619 #endif
1620 
1621  // get older style project title
1622  QString oldTitle;
1623  _getTitle( *doc, oldTitle );
1624 
1625  readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
1626 
1627  const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1628  if ( homePathNl.count() > 0 )
1629  {
1630  const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1631  const QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1632  if ( !homePath.isEmpty() )
1634  }
1635  else
1636  {
1637  emit homePathChanged();
1638  }
1639 
1640  const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1641  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1642  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1644  const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1645  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1646  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1647  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1649 
1650  QgsReadWriteContext context;
1651  context.setPathResolver( pathResolver() );
1652  context.setProjectTranslator( this );
1653 
1654  //crs
1655  QgsCoordinateReferenceSystem projectCrs;
1656  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1657  {
1658  // first preference - dedicated projectCrs node
1659  const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1660  if ( !srsNode.isNull() )
1661  {
1662  projectCrs.readXml( srsNode );
1663  }
1664 
1665  if ( !projectCrs.isValid() )
1666  {
1667  const QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1668  const long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1669  const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1670 
1671  // authid should be prioritized over all
1672  const bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
1673  if ( !authid.isEmpty() && !isUserAuthId )
1674  projectCrs = QgsCoordinateReferenceSystem( authid );
1675 
1676  // try the CRS
1677  if ( !projectCrs.isValid() && currentCRS >= 0 )
1678  {
1679  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1680  }
1681 
1682  // if that didn't produce a match, try the proj.4 string
1683  if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
1684  {
1685  projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
1686  }
1687 
1688  // last just take the given id
1689  if ( !projectCrs.isValid() )
1690  {
1691  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1692  }
1693  }
1694  }
1695  mCrs = projectCrs;
1696 
1697  QStringList datumErrors;
1698  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1699  {
1700  emit missingDatumTransforms( datumErrors );
1701  }
1702  emit transformContextChanged();
1703 
1704  //add variables defined in project file - do this early in the reading cycle, as other components
1705  //(e.g. layouts) may depend on these variables
1706  const QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1707  const QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1708 
1709  mCustomVariables.clear();
1710  if ( variableNames.length() == variableValues.length() )
1711  {
1712  for ( int i = 0; i < variableNames.length(); ++i )
1713  {
1714  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1715  }
1716  }
1717  else
1718  {
1719  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1720  }
1721 
1722  QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectMetadata" ) );
1723 
1724  if ( !element.isNull() )
1725  {
1726  mMetadata.readMetadataXml( element );
1727  }
1728  else
1729  {
1730  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1731  mMetadata = QgsProjectMetadata();
1732  }
1733  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1734  {
1735  // upgrade older title storage to storing within project metadata.
1736  mMetadata.setTitle( oldTitle );
1737  }
1738  emit metadataChanged();
1739 
1740  // Transaction mode
1741  element = doc->documentElement().firstChildElement( QStringLiteral( "transaction" ) );
1742  if ( !element.isNull() )
1743  {
1744  mTransactionMode = qgsEnumKeyToValue( element.attribute( QStringLiteral( "mode" ) ), Qgis::TransactionMode::Disabled );
1745  }
1746  else
1747  {
1748  // maybe older project => try read autotransaction
1749  element = doc->documentElement().firstChildElement( QStringLiteral( "autotransaction" ) );
1750  if ( ! element.isNull() )
1751  {
1752  mTransactionMode = static_cast<Qgis::TransactionMode>( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() );
1753  }
1754  }
1755 
1756  element = doc->documentElement().firstChildElement( QStringLiteral( "projectFlags" ) );
1757  if ( !element.isNull() )
1758  {
1759  mFlags = qgsFlagKeysToValue( element.attribute( QStringLiteral( "set" ) ), Qgis::ProjectFlags() );
1760  }
1761  else
1762  {
1763  // older project compatibility
1764  element = doc->documentElement().firstChildElement( QStringLiteral( "evaluateDefaultValues" ) );
1765  if ( !element.isNull() )
1766  {
1767  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1769  }
1770 
1771  // Read trust layer metadata config in the project
1772  element = doc->documentElement().firstChildElement( QStringLiteral( "trust" ) );
1773  if ( !element.isNull() )
1774  {
1775  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1777  }
1778  }
1779 
1780  // read the layer tree from project file
1781  profile.switchTask( tr( "Loading layer tree" ) );
1782  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1783 
1784  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1785  if ( !layerTreeElem.isNull() )
1786  {
1787  // Use a temporary tree to read the nodes to prevent signals being delivered to the models
1788  QgsLayerTree tempTree;
1789  tempTree.readChildrenFromXml( layerTreeElem, context );
1790  mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
1791  }
1792  else
1793  {
1794  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1795  }
1796 
1797  mLayerTreeRegistryBridge->setEnabled( false );
1798 
1799  // get the map layers
1800  profile.switchTask( tr( "Reading map layers" ) );
1801 
1802  QList<QDomNode> brokenNodes;
1803  const bool clean = _getMapLayers( *doc, brokenNodes, flags );
1804 
1805  // review the integrity of the retrieved map layers
1806  if ( !clean )
1807  {
1808  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1809 
1810  if ( !brokenNodes.isEmpty() )
1811  {
1812  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1813  }
1814 
1815  // we let a custom handler decide what to do with missing layers
1816  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1817  mBadLayerHandler->handleBadLayers( brokenNodes );
1818  }
1819 
1820  mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
1821  mMainAnnotationLayer->setTransformContext( mTransformContext );
1822 
1823  // load embedded groups and layers
1824  profile.switchTask( tr( "Loading embedded layers" ) );
1825  loadEmbeddedNodes( mRootGroup, flags );
1826 
1827  // Resolve references to other layers
1828  // Needs to be done here once all dependent layers are loaded
1829  profile.switchTask( tr( "Resolving layer references" ) );
1830  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1831  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
1832  {
1833  it.value()->resolveReferences( this );
1834  }
1835 
1836  mLayerTreeRegistryBridge->setEnabled( true );
1837 
1838  // now that layers are loaded, we can resolve layer tree's references to the layers
1839  profile.switchTask( tr( "Resolving references" ) );
1840  mRootGroup->resolveReferences( this );
1841 
1842  if ( !layerTreeElem.isNull() )
1843  {
1844  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1845  }
1846 
1847  // Load pre 3.0 configuration
1848  const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1849  if ( !layerTreeCanvasElem.isNull( ) )
1850  {
1851  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1852  }
1853 
1854  // Convert pre 3.4 to create layers flags
1855  if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
1856  {
1857  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1858  for ( const QString &layerId : requiredLayerIds )
1859  {
1860  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1861  {
1862  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1863  }
1864  }
1865  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1866  for ( const QString &layerId : disabledLayerIds )
1867  {
1868  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1869  {
1870  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1871  }
1872  }
1873  }
1874 
1875  // Convert pre 3.26 default styles
1876  if ( QgsProjectVersion( 3, 26, 0 ) > mSaveVersion )
1877  {
1878  // Convert default symbols
1879  QString styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
1880  if ( !styleName.isEmpty() )
1881  {
1882  std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1884  }
1885  styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
1886  if ( !styleName.isEmpty() )
1887  {
1888  std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1890  }
1891  styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
1892  if ( !styleName.isEmpty() )
1893  {
1894  std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1896  }
1897  styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
1898  if ( !styleName.isEmpty() )
1899  {
1900  std::unique_ptr<QgsColorRamp> colorRamp( QgsStyle::defaultStyle()->colorRamp( styleName ) );
1901  styleSettings()->setDefaultColorRamp( colorRamp.get() );
1902  }
1903 
1904  // Convert randomize default symbol fill color
1905  styleSettings()->setRandomizeDefaultSymbolColor( readBoolEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ), true ) );
1906 
1907  // Convert default symbol opacity
1908  double opacity = 1.0;
1909  bool ok = false;
1910  // upgrade old setting
1911  double alpha = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ), 255, &ok );
1912  if ( ok )
1913  opacity = alpha / 255.0;
1914  double newOpacity = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ), 1.0, &ok );
1915  if ( ok )
1916  opacity = newOpacity;
1917  styleSettings()->setDefaultSymbolOpacity( opacity );
1918 
1919  // Cleanup
1920  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
1921  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
1922  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
1923  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
1924  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ) );
1925  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ) );
1926  removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ) );
1927  }
1928 
1929  // After bad layer handling we might still have invalid layers,
1930  // store them in case the user wanted to handle them later
1931  // or wanted to pass them through when saving
1932  if ( !( flags & Qgis::ProjectReadFlag::DontStoreOriginalStyles ) )
1933  {
1934  profile.switchTask( tr( "Storing original layer properties" ) );
1935  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1936  }
1937 
1938  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1939 
1940  profile.switchTask( tr( "Loading map themes" ) );
1941  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1943  mMapThemeCollection->readXml( *doc );
1944 
1945  profile.switchTask( tr( "Loading label settings" ) );
1946  mLabelingEngineSettings->readSettingsFromProject( this );
1948 
1949  profile.switchTask( tr( "Loading annotations" ) );
1950  mAnnotationManager->readXml( doc->documentElement(), context );
1951  if ( !( flags & Qgis::ProjectReadFlag::DontLoadLayouts ) )
1952  {
1953  profile.switchTask( tr( "Loading layouts" ) );
1954  mLayoutManager->readXml( doc->documentElement(), *doc );
1955  }
1956 
1957  if ( !( flags & Qgis::ProjectReadFlag::DontLoad3DViews ) )
1958  {
1959  profile.switchTask( tr( "Loading 3D Views" ) );
1960  m3DViewsManager->readXml( doc->documentElement(), *doc );
1961  }
1962 
1963  profile.switchTask( tr( "Loading bookmarks" ) );
1964  mBookmarkManager->readXml( doc->documentElement(), *doc );
1965 
1966  // reassign change dependencies now that all layers are loaded
1967  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1968  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
1969  {
1970  it.value()->setDependencies( it.value()->dependencies() );
1971  }
1972 
1973  profile.switchTask( tr( "Loading snapping settings" ) );
1974  mSnappingConfig.readProject( *doc );
1975  mAvoidIntersectionsMode = static_cast<Qgis::AvoidIntersectionsMode>( readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( Qgis::AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
1976 
1977  profile.switchTask( tr( "Loading view settings" ) );
1978  // restore older project scales settings
1979  mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
1980  const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
1981  QVector<double> res;
1982  for ( const QString &scale : scales )
1983  {
1984  const QStringList parts = scale.split( ':' );
1985  if ( parts.size() != 2 )
1986  continue;
1987 
1988  bool ok = false;
1989  const double denominator = QLocale().toDouble( parts[1], &ok );
1990  if ( ok )
1991  {
1992  res << denominator;
1993  }
1994  }
1995  mViewSettings->setMapScales( res );
1996  const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
1997  if ( !viewSettingsElement.isNull() )
1998  mViewSettings->readXml( viewSettingsElement, context );
1999 
2000  // restore style settings
2001  profile.switchTask( tr( "Loading style properties" ) );
2002  const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectStyleSettings" ) );
2003  if ( !styleSettingsElement.isNull() )
2004  mStyleSettings->readXml( styleSettingsElement, context, flags );
2005 
2006  // restore time settings
2007  profile.switchTask( tr( "Loading temporal settings" ) );
2008  const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectTimeSettings" ) );
2009  if ( !timeSettingsElement.isNull() )
2010  mTimeSettings->readXml( timeSettingsElement, context );
2011 
2012 
2013  profile.switchTask( tr( "Loading elevation properties" ) );
2014  const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( QStringLiteral( "ElevationProperties" ) );
2015  if ( !elevationPropertiesElement.isNull() )
2016  mElevationProperties->readXml( elevationPropertiesElement, context );
2017  mElevationProperties->resolveReferences( this );
2018 
2019  profile.switchTask( tr( "Loading display settings" ) );
2020  const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
2021  if ( !displaySettingsElement.isNull() )
2022  mDisplaySettings->readXml( displaySettingsElement, context );
2023 
2024  profile.switchTask( tr( "Updating variables" ) );
2025  emit customVariablesChanged();
2026  profile.switchTask( tr( "Updating CRS" ) );
2027  emit crsChanged();
2028  emit ellipsoidChanged( ellipsoid() );
2029 
2030  // read the project: used by map canvas and legend
2031  profile.switchTask( tr( "Reading external settings" ) );
2032  emit readProject( *doc );
2033  emit readProjectWithContext( *doc, context );
2034 
2035  profile.switchTask( tr( "Updating interface" ) );
2036 
2037  snapSignalBlock.release();
2038  if ( !mBlockSnappingUpdates )
2039  emit snappingConfigChanged( mSnappingConfig );
2040 
2043  emit projectColorsChanged();
2044 
2045  // if all went well, we're allegedly in pristine state
2046  if ( clean )
2047  setDirty( false );
2048 
2049  QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUser ), 2 );
2050  QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUserFull ), 2 );
2051 
2055 
2056  if ( mTranslator )
2057  {
2058  //project possibly translated -> rename it with locale postfix
2059  const QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
2060  setFileName( newFileName );
2061 
2062  if ( write() )
2063  {
2064  setTitle( localeFileName );
2065  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Success );
2066  }
2067  else
2068  {
2069  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Critical );
2070  }
2071  }
2072 
2073  // lastly, make any previously editable layers editable
2074  const QMap<QString, QgsMapLayer *> loadedLayers = mapLayers();
2075  for ( auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2076  {
2077  if ( it.value()->isValid() && it.value()->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2078  {
2079  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2080  vl->startEditing();
2081  it.value()->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2082  }
2083  }
2084 
2085  return true;
2086 }
2087 
2088 
2089 bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
2090 {
2091  bool valid = true;
2092  const auto constChildren = group->children();
2093  for ( QgsLayerTreeNode *child : constChildren )
2094  {
2095  if ( QgsLayerTree::isGroup( child ) )
2096  {
2097  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
2098  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2099  {
2100  // make sure to convert the path from relative to absolute
2101  const QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
2102  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
2103  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
2104  if ( newGroup )
2105  {
2106  QList<QgsLayerTreeNode *> clonedChildren;
2107  const QList<QgsLayerTreeNode *> constChildren = newGroup->children();
2108  clonedChildren.reserve( constChildren.size() );
2109  for ( QgsLayerTreeNode *newGroupChild : constChildren )
2110  clonedChildren << newGroupChild->clone();
2111  delete newGroup;
2112 
2113  childGroup->insertChildNodes( 0, clonedChildren );
2114  }
2115  }
2116  else
2117  {
2118  loadEmbeddedNodes( childGroup, flags );
2119  }
2120  }
2121  else if ( QgsLayerTree::isLayer( child ) )
2122  {
2123  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2124  {
2125  QList<QDomNode> brokenNodes;
2126  if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
2127  {
2128  valid = valid && false;
2129  }
2130  }
2131  }
2132 
2133  }
2134 
2135  return valid;
2136 }
2137 
2138 QVariantMap QgsProject::customVariables() const
2139 {
2140  return mCustomVariables;
2141 }
2142 
2143 void QgsProject::setCustomVariables( const QVariantMap &variables )
2144 {
2145  if ( variables == mCustomVariables )
2146  return;
2147 
2148  //write variable to project
2149  QStringList variableNames;
2150  QStringList variableValues;
2151 
2152  QVariantMap::const_iterator it = variables.constBegin();
2153  for ( ; it != variables.constEnd(); ++it )
2154  {
2155  variableNames << it.key();
2156  variableValues << it.value().toString();
2157  }
2158 
2159  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
2160  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
2161 
2162  mCustomVariables = variables;
2163  mProjectScope.reset();
2164 
2165  emit customVariablesChanged();
2166 }
2167 
2169 {
2170  *mLabelingEngineSettings = settings;
2172 }
2173 
2175 {
2176  return *mLabelingEngineSettings;
2177 }
2178 
2180 {
2181  mProjectScope.reset();
2182  return mLayerStore.get();
2183 }
2184 
2186 {
2187  return mLayerStore.get();
2188 }
2189 
2190 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
2191 {
2192  QList<QgsVectorLayer *> layers;
2193  const QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
2194  const auto constLayerIds = layerIds;
2195  for ( const QString &layerId : constLayerIds )
2196  {
2197  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
2198  layers << vlayer;
2199  }
2200  return layers;
2201 }
2202 
2203 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
2204 {
2205  QStringList list;
2206  list.reserve( layers.size() );
2207  for ( QgsVectorLayer *layer : layers )
2208  list << layer->id();
2209  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
2211 }
2212 
2214 {
2215  QgsExpressionContext context;
2216 
2219 
2220  return context;
2221 }
2222 
2224 {
2225  // MUCH cheaper to clone than build
2226  if ( mProjectScope )
2227  {
2228  std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2229  // we can't cache these
2230  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
2231  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
2232  return projectScope.release();
2233  }
2234 
2235  mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
2236 
2237  const QVariantMap vars = customVariables();
2238 
2239  QVariantMap::const_iterator it = vars.constBegin();
2240 
2241  for ( ; it != vars.constEnd(); ++it )
2242  {
2243  mProjectScope->setVariable( it.key(), it.value(), true );
2244  }
2245 
2246  QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
2247  if ( projectPath.isEmpty() )
2248  projectPath = mOriginalPath;
2249  const QString projectFolder = QFileInfo( projectPath ).path();
2250  const QString projectFilename = QFileInfo( projectPath ).fileName();
2251  const QString projectBasename = baseName();
2252 
2253  //add other known project variables
2254  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
2255  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
2256  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
2257  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
2258  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
2259  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
2260  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_last_saved" ), mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
2261  const QgsCoordinateReferenceSystem projectCrs = crs();
2262  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
2263  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
2264  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_description" ), projectCrs.description(), true, true ) );
2265  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
2266  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
2267  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_units" ), QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
2268  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_acronym" ), projectCrs.projectionAcronym(), true ) );
2269  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_ellipsoid" ), projectCrs.ellipsoidAcronym(), true ) );
2270  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_proj4" ), projectCrs.toProj(), true ) );
2271  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_wkt" ), projectCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), true ) );
2272 
2273  // metadata
2274  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
2275  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
2276  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
2277  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
2278 
2279  // keywords
2280  QVariantMap keywords;
2281  const QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2282  for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2283  {
2284  keywords.insert( it.key(), it.value() );
2285  }
2286  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
2287 
2288  // layers
2289  QVariantList layersIds;
2290  QVariantList layers;
2291  const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2292  layersIds.reserve( layersInProject.count() );
2293  layers.reserve( layersInProject.count() );
2294  for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2295  {
2296  layersIds << it.value()->id();
2297  layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( it.value() ) );
2298  }
2299  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_ids" ), layersIds, true ) );
2300  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
2301 
2302  mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
2303 
2305 }
2306 
2307 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
2308 {
2309  const QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2310 
2311  const auto constLayers = layers;
2312  for ( QgsMapLayer *layer : constLayers )
2313  {
2314  if ( ! layer->isValid() )
2315  return;
2316 
2317  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
2318 
2319  // check if we have to update connections for layers with dependencies
2320  for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2321  {
2322  const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2323  if ( deps.contains( layer->id() ) )
2324  {
2325  // reconnect to change signals
2326  it.value()->setDependencies( deps );
2327  }
2328  }
2329  }
2330 
2331  updateTransactionGroups();
2332 
2333  if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
2334  emit snappingConfigChanged( mSnappingConfig );
2335 }
2336 
2337 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
2338 {
2339  if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
2340  emit snappingConfigChanged( mSnappingConfig );
2341 }
2342 
2343 void QgsProject::cleanTransactionGroups( bool force )
2344 {
2345  bool changed = false;
2346  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2347  {
2348  if ( tg.value()->isEmpty() || force )
2349  {
2350  delete tg.value();
2351  tg = mTransactionGroups.erase( tg );
2352  changed = true;
2353  }
2354  else
2355  {
2356  ++tg;
2357  }
2358  }
2359  if ( changed )
2360  emit transactionGroupsChanged();
2361 }
2362 
2363 void QgsProject::updateTransactionGroups()
2364 {
2365  mEditBufferGroup.clear();
2366 
2367  switch ( mTransactionMode )
2368  {
2370  {
2371  cleanTransactionGroups( true );
2372  return;
2373  }
2374  break;
2376  cleanTransactionGroups( true );
2377  break;
2379  cleanTransactionGroups( false );
2380  break;
2381  }
2382 
2383  bool tgChanged = false;
2384  const auto constLayers = mapLayers().values();
2385  for ( QgsMapLayer *layer : constLayers )
2386  {
2387  if ( ! layer->isValid() )
2388  continue;
2389 
2390  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2391  if ( ! vlayer )
2392  continue;
2393 
2394  switch ( mTransactionMode )
2395  {
2397  Q_ASSERT( false );
2398  break;
2400  {
2401  if ( QgsTransaction::supportsTransaction( vlayer ) )
2402  {
2403  const QString connString = QgsTransaction::connectionString( vlayer->source() );
2404  const QString key = vlayer->providerType();
2405 
2406  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
2407 
2408  if ( !tg )
2409  {
2410  tg = new QgsTransactionGroup();
2411  mTransactionGroups.insert( qMakePair( key, connString ), tg );
2412  tgChanged = true;
2413  }
2414  tg->addLayer( vlayer );
2415  }
2416  }
2417  break;
2419  {
2420  if ( vlayer->supportsEditing() )
2421  mEditBufferGroup.addLayer( vlayer );
2422  }
2423  break;
2424  }
2425  }
2426 
2427  if ( tgChanged )
2428  emit transactionGroupsChanged();
2429 }
2430 
2431 bool QgsProject::readLayer( const QDomNode &layerNode )
2432 {
2433  QgsReadWriteContext context;
2434  context.setPathResolver( pathResolver() );
2435  context.setProjectTranslator( this );
2437  QList<QDomNode> brokenNodes;
2438  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
2439  {
2440  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
2441  // added layer for joins
2442  const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
2443  for ( QgsVectorLayer *layer : vectorLayers )
2444  {
2445  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
2446  layer->resolveReferences( this );
2447 
2448  if ( layer->isValid() && layer->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2449  {
2450  layer->startEditing();
2451  layer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2452  }
2453  }
2454  return true;
2455  }
2456  return false;
2457 }
2458 
2459 bool QgsProject::write( const QString &filename )
2460 {
2461  mFile.setFileName( filename );
2462  mCachedHomePath.clear();
2463  return write();
2464 }
2465 
2467 {
2468  mProjectScope.reset();
2469  if ( QgsProjectStorage *storage = projectStorage() )
2470  {
2471  QgsReadWriteContext context;
2472  // for projects stored in a custom storage, we have to check for the support
2473  // of relative paths since the storage most likely will not be in a file system
2474  const QString storageFilePath { storage->filePath( mFile.fileName() ) };
2475  if ( storageFilePath.isEmpty() )
2476  {
2478  }
2479  context.setPathResolver( pathResolver() );
2480 
2481  const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
2482  const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
2483 
2484  if ( !zip( tmpZipFilename ) )
2485  return false; // zip() already calls setError() when returning false
2486 
2487  QFile tmpZipFile( tmpZipFilename );
2488  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
2489  {
2490  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
2491  return false;
2492  }
2493 
2495  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
2496  {
2497  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
2498  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2499  if ( !messages.isEmpty() )
2500  err += QStringLiteral( "\n\n" ) + messages.last().message();
2501  setError( err );
2502  return false;
2503  }
2504 
2505  tmpZipFile.close();
2506  QFile::remove( tmpZipFilename );
2507 
2508  return true;
2509  }
2510 
2511  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2512  {
2513  return zip( mFile.fileName() );
2514  }
2515  else
2516  {
2517  // write project file even if the auxiliary storage is not correctly
2518  // saved
2519  const bool asOk = saveAuxiliaryStorage();
2520  const bool writeOk = writeProjectFile( mFile.fileName() );
2521  bool attachmentsOk = true;
2522  if ( !mArchive->files().isEmpty() )
2523  {
2524  const QFileInfo finfo( mFile.fileName() );
2525  const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
2526  attachmentsOk = mArchive->zip( attachmentsZip );
2527  }
2528 
2529  // errors raised during writing project file are more important
2530  if ( ( !asOk || !attachmentsOk ) && writeOk )
2531  {
2532  QStringList errorMessage;
2533  if ( !asOk )
2534  {
2535  const QString err = mAuxiliaryStorage->errorString();
2536  errorMessage.append( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2537  }
2538  if ( !attachmentsOk )
2539  {
2540  errorMessage.append( tr( "Unable to save attachments archive" ) );
2541  }
2542  setError( errorMessage.join( '\n' ) );
2543  }
2544 
2545  return asOk && writeOk && attachmentsOk;
2546  }
2547 }
2548 
2549 bool QgsProject::writeProjectFile( const QString &filename )
2550 {
2551  QFile projectFile( filename );
2552  clearError();
2553 
2554  // if we have problems creating or otherwise writing to the project file,
2555  // let's find out up front before we go through all the hand-waving
2556  // necessary to create all the Dom objects
2557  const QFileInfo myFileInfo( projectFile );
2558  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
2559  {
2560  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
2561  .arg( projectFile.fileName() ) );
2562  return false;
2563  }
2564 
2565  QgsReadWriteContext context;
2566  context.setPathResolver( pathResolver() );
2568 
2569  QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
2570 
2571  const QDomDocumentType documentType =
2572  QDomImplementation().createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
2573  QStringLiteral( "SYSTEM" ) );
2574  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
2575 
2576  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
2577  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
2578  qgisNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
2579 
2580  if ( !mSettings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
2581  {
2582  const QString newSaveUser = QgsApplication::userLoginName();
2583  const QString newSaveUserFull = QgsApplication::userFullName();
2584  qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2585  qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2586  mSaveUser = newSaveUser;
2587  mSaveUserFull = newSaveUserFull;
2588  mSaveDateTime = QDateTime::currentDateTime();
2589  qgisNode.setAttribute( QStringLiteral( "saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
2590  }
2591  else
2592  {
2593  mSaveUser.clear();
2594  mSaveUserFull.clear();
2595  mSaveDateTime = QDateTime();
2596  }
2597  doc->appendChild( qgisNode );
2598  mSaveVersion = QgsProjectVersion( Qgis::version() );
2599 
2600  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2601  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2602  qgisNode.appendChild( homePathNode );
2603 
2604  // title
2605  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2606  qgisNode.appendChild( titleNode );
2607 
2608  QDomElement transactionNode = doc->createElement( QStringLiteral( "transaction" ) );
2609  transactionNode.setAttribute( QStringLiteral( "mode" ), qgsEnumValueToKey( mTransactionMode ) );
2610  qgisNode.appendChild( transactionNode );
2611 
2612  QDomElement flagsNode = doc->createElement( QStringLiteral( "projectFlags" ) );
2613  flagsNode.setAttribute( QStringLiteral( "set" ), qgsFlagValueToKeys( mFlags ) );
2614  qgisNode.appendChild( flagsNode );
2615 
2616  const QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
2617  titleNode.appendChild( titleText );
2618 
2619  // write project CRS
2620  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
2621  mCrs.writeXml( srsNode, *doc );
2622  qgisNode.appendChild( srsNode );
2623 
2624  // write layer tree - make sure it is without embedded subgroups
2625  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
2627  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
2628 
2629  clonedRoot->writeXml( qgisNode, context );
2630  delete clonedRoot;
2631 
2632  mSnappingConfig.writeProject( *doc );
2633  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( mAvoidIntersectionsMode ) );
2634 
2635  // let map canvas and legend write their information
2636  emit writeProject( *doc );
2637 
2638  // within top level node save list of layers
2639  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2640 
2641  QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
2642  mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
2643  qgisNode.appendChild( annotationLayerNode );
2644 
2645  // Iterate over layers in zOrder
2646  // Call writeXml() on each
2647  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
2648 
2649  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
2650  while ( li != layers.end() )
2651  {
2652  QgsMapLayer *ml = li.value();
2653 
2654  if ( ml )
2655  {
2656  const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
2657  if ( emIt == mEmbeddedLayers.constEnd() )
2658  {
2659  QDomElement maplayerElem;
2660  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
2661  // not available, just write what we DO have
2662  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
2663  {
2664  // general layer metadata
2665  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2666  ml->writeLayerXml( maplayerElem, *doc, context );
2667 
2669  maplayerElem.setAttribute( QStringLiteral( "editable" ), QStringLiteral( "1" ) );
2670  }
2671  else if ( ! ml->originalXmlProperties().isEmpty() )
2672  {
2673  QDomDocument document;
2674  if ( document.setContent( ml->originalXmlProperties() ) )
2675  {
2676  maplayerElem = document.firstChildElement();
2677  }
2678  else
2679  {
2680  QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
2681  }
2682  }
2683 
2684  emit writeMapLayer( ml, maplayerElem, *doc );
2685 
2686  projectLayersNode.appendChild( maplayerElem );
2687  }
2688  else
2689  {
2690  // layer defined in an external project file
2691  // only save embedded layer if not managed by a legend group
2692  if ( emIt.value().second )
2693  {
2694  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2695  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
2696  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
2697  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
2698  projectLayersNode.appendChild( mapLayerElem );
2699  }
2700  }
2701  }
2702  li++;
2703  }
2704 
2705  qgisNode.appendChild( projectLayersNode );
2706 
2707  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
2708  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
2709  for ( QgsMapLayer *layer : constCustomLayerOrder )
2710  {
2711  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
2712  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
2713  layerOrderNode.appendChild( mapLayerElem );
2714  }
2715  qgisNode.appendChild( layerOrderNode );
2716 
2717  mLabelingEngineSettings->writeSettingsToProject( this );
2718 
2719  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
2720  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
2721  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
2722 
2723  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
2724  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
2725  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
2726  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
2727 
2728  // now add the optional extra properties
2729 #if 0
2730  dump_( mProperties );
2731 #endif
2732 
2733  QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 2 );
2734 
2735  if ( !mProperties.isEmpty() ) // only worry about properties if we
2736  // actually have any properties
2737  {
2738  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2739  }
2740 
2741  QDomElement ddElem = doc->createElement( QStringLiteral( "dataDefinedServerProperties" ) );
2742  mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
2743  qgisNode.appendChild( ddElem );
2744 
2745  mMapThemeCollection->writeXml( *doc );
2746 
2747  mTransformContext.writeXml( qgisNode, context );
2748 
2749  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2750  mMetadata.writeMetadataXml( metadataElem, *doc );
2751  qgisNode.appendChild( metadataElem );
2752 
2753  const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2754  qgisNode.appendChild( annotationsElem );
2755 
2756  const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2757  qgisNode.appendChild( layoutElem );
2758 
2759  const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
2760  qgisNode.appendChild( views3DElem );
2761 
2762  const QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2763  qgisNode.appendChild( bookmarkElem );
2764 
2765  const QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
2766  qgisNode.appendChild( viewSettingsElem );
2767 
2768  const QDomElement styleSettingsElem = mStyleSettings->writeXml( *doc, context );
2769  qgisNode.appendChild( styleSettingsElem );
2770 
2771  const QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
2772  qgisNode.appendChild( timeSettingsElement );
2773 
2774  const QDomElement elevationPropertiesElement = mElevationProperties->writeXml( *doc, context );
2775  qgisNode.appendChild( elevationPropertiesElement );
2776 
2777  const QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
2778  qgisNode.appendChild( displaySettingsElem );
2779 
2780  // now wrap it up and ship it to the project file
2781  doc->normalize(); // XXX I'm not entirely sure what this does
2782 
2783  // Create backup file
2784  if ( QFile::exists( fileName() ) )
2785  {
2786  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2787  bool ok = true;
2788  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2789  ok &= projectFile.open( QIODevice::ReadOnly );
2790 
2791  QByteArray ba;
2792  while ( ok && !projectFile.atEnd() )
2793  {
2794  ba = projectFile.read( 10240 );
2795  ok &= backupFile.write( ba ) == ba.size();
2796  }
2797 
2798  projectFile.close();
2799  backupFile.close();
2800 
2801  if ( !ok )
2802  {
2803  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2804  return false;
2805  }
2806 
2807  const QFileInfo fi( fileName() );
2808  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2809  utime( backupFile.fileName().toUtf8().constData(), &tb );
2810  }
2811 
2812  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2813  {
2814  projectFile.close(); // even though we got an error, let's make
2815  // sure it's closed anyway
2816 
2817  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2818  return false;
2819  }
2820 
2821  QTemporaryFile tempFile;
2822  bool ok = tempFile.open();
2823  if ( ok )
2824  {
2825  QTextStream projectFileStream( &tempFile );
2826  doc->save( projectFileStream, 2 ); // save as utf-8
2827  ok &= projectFileStream.pos() > -1;
2828 
2829  ok &= tempFile.seek( 0 );
2830 
2831  QByteArray ba;
2832  while ( ok && !tempFile.atEnd() )
2833  {
2834  ba = tempFile.read( 10240 );
2835  ok &= projectFile.write( ba ) == ba.size();
2836  }
2837 
2838  ok &= projectFile.error() == QFile::NoError;
2839 
2840  projectFile.close();
2841  }
2842 
2843  tempFile.close();
2844 
2845  if ( !ok )
2846  {
2847  setError( tr( "Unable to save to file %1. Your project "
2848  "may be corrupted on disk. Try clearing some space on the volume and "
2849  "check file permissions before pressing save again." )
2850  .arg( projectFile.fileName() ) );
2851  return false;
2852  }
2853 
2854  setDirty( false ); // reset to pristine state
2855 
2856  emit projectSaved();
2857  return true;
2858 }
2859 
2860 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2861 {
2862  bool propertiesModified;
2863  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2864 
2865  if ( propertiesModified )
2866  setDirty( true );
2867 
2868  return success;
2869 }
2870 
2871 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2872 {
2873  bool propertiesModified;
2874  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2875 
2876  if ( propertiesModified )
2877  setDirty( true );
2878 
2879  return success;
2880 }
2881 
2882 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2883 {
2884  bool propertiesModified;
2885  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2886 
2887  if ( propertiesModified )
2888  setDirty( true );
2889 
2890  return success;
2891 }
2892 
2893 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2894 {
2895  bool propertiesModified;
2896  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2897 
2898  if ( propertiesModified )
2899  setDirty( true );
2900 
2901  return success;
2902 }
2903 
2904 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2905 {
2906  bool propertiesModified;
2907  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2908 
2909  if ( propertiesModified )
2910  setDirty( true );
2911 
2912  return success;
2913 }
2914 
2915 QStringList QgsProject::readListEntry( const QString &scope,
2916  const QString &key,
2917  const QStringList &def,
2918  bool *ok ) const
2919 {
2920  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2921 
2922  QVariant value;
2923 
2924  if ( property )
2925  {
2926  value = property->value();
2927 
2928  const bool valid = QVariant::StringList == value.type();
2929  if ( ok )
2930  *ok = valid;
2931 
2932  if ( valid )
2933  {
2934  return value.toStringList();
2935  }
2936  }
2937  else if ( ok )
2938  *ok = false;
2939 
2940 
2941  return def;
2942 }
2943 
2944 
2945 QString QgsProject::readEntry( const QString &scope,
2946  const QString &key,
2947  const QString &def,
2948  bool *ok ) const
2949 {
2950  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2951 
2952  QVariant value;
2953 
2954  if ( property )
2955  {
2956  value = property->value();
2957 
2958  const bool valid = value.canConvert( QVariant::String );
2959  if ( ok )
2960  *ok = valid;
2961 
2962  if ( valid )
2963  return value.toString();
2964  }
2965  else if ( ok )
2966  *ok = false;
2967 
2968  return def;
2969 }
2970 
2971 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2972  bool *ok ) const
2973 {
2974  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2975 
2976  QVariant value;
2977 
2978  if ( property )
2979  {
2980  value = property->value();
2981  }
2982 
2983  const bool valid = value.canConvert( QVariant::Int );
2984 
2985  if ( ok )
2986  {
2987  *ok = valid;
2988  }
2989 
2990  if ( valid )
2991  {
2992  return value.toInt();
2993  }
2994 
2995  return def;
2996 }
2997 
2998 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2999  double def,
3000  bool *ok ) const
3001 {
3002  QgsProjectProperty *property = findKey_( scope, key, mProperties );
3003  if ( property )
3004  {
3005  const QVariant value = property->value();
3006 
3007  const bool valid = value.canConvert( QVariant::Double );
3008  if ( ok )
3009  *ok = valid;
3010 
3011  if ( valid )
3012  return value.toDouble();
3013  }
3014  else if ( ok )
3015  *ok = false;
3016 
3017  return def;
3018 }
3019 
3020 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
3021  bool *ok ) const
3022 {
3023  QgsProjectProperty *property = findKey_( scope, key, mProperties );
3024 
3025  if ( property )
3026  {
3027  const QVariant value = property->value();
3028 
3029  const bool valid = value.canConvert( QVariant::Bool );
3030  if ( ok )
3031  *ok = valid;
3032 
3033  if ( valid )
3034  return value.toBool();
3035  }
3036  else if ( ok )
3037  *ok = false;
3038 
3039  return def;
3040 }
3041 
3042 bool QgsProject::removeEntry( const QString &scope, const QString &key )
3043 {
3044  if ( findKey_( scope, key, mProperties ) )
3045  {
3046  removeKey_( scope, key, mProperties );
3047  setDirty( true );
3048  }
3049 
3050  return !findKey_( scope, key, mProperties );
3051 }
3052 
3053 
3054 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
3055 {
3056  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3057 
3058  QStringList entries;
3059 
3060  if ( foundProperty )
3061  {
3062  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3063 
3064  if ( propertyKey )
3065  { propertyKey->entryList( entries ); }
3066  }
3067 
3068  return entries;
3069 }
3070 
3071 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
3072 {
3073  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3074 
3075  QStringList entries;
3076 
3077  if ( foundProperty )
3078  {
3079  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3080 
3081  if ( propertyKey )
3082  { propertyKey->subkeyList( entries ); }
3083  }
3084 
3085  return entries;
3086 }
3087 
3089 {
3090  dump_( mProperties );
3091 }
3092 
3094 {
3095  QString filePath;
3096  switch ( filePathStorage() )
3097  {
3099  break;
3100 
3102  {
3103  // for projects stored in a custom storage, we need to ask to the
3104  // storage for the path, if the storage returns an empty path
3105  // relative paths are not supported
3106  if ( QgsProjectStorage *storage = projectStorage() )
3107  {
3108  filePath = storage->filePath( mFile.fileName() );
3109  }
3110  else
3111  {
3112  filePath = fileName();
3113  }
3114  break;
3115  }
3116  }
3117 
3118  return QgsPathResolver( filePath, mArchive->dir() );
3119 }
3120 
3121 QString QgsProject::readPath( const QString &src ) const
3122 {
3123  return pathResolver().readPath( src );
3124 }
3125 
3126 QString QgsProject::writePath( const QString &src ) const
3127 {
3128  return pathResolver().writePath( src );
3129 }
3130 
3131 void QgsProject::setError( const QString &errorMessage )
3132 {
3133  mErrorMessage = errorMessage;
3134 }
3135 
3136 QString QgsProject::error() const
3137 {
3138  return mErrorMessage;
3139 }
3140 
3141 void QgsProject::clearError()
3142 {
3143  setError( QString() );
3144 }
3145 
3147 {
3148  delete mBadLayerHandler;
3149  mBadLayerHandler = handler;
3150 }
3151 
3152 QString QgsProject::layerIsEmbedded( const QString &id ) const
3153 {
3154  const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
3155  if ( it == mEmbeddedLayers.constEnd() )
3156  {
3157  return QString();
3158  }
3159  return it.value().first;
3160 }
3161 
3162 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
3163  bool saveFlag, Qgis::ProjectReadFlags flags )
3164 {
3165  QgsDebugCall;
3166 
3167  static QString sPrevProjectFilePath;
3168  static QDateTime sPrevProjectFileTimestamp;
3169  static QDomDocument sProjectDocument;
3170 
3171  QString qgsProjectFile = projectFilePath;
3172  QgsProjectArchive archive;
3173  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3174  {
3175  archive.unzip( projectFilePath );
3176  qgsProjectFile = archive.projectFile();
3177  }
3178 
3179  const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
3180 
3181  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
3182  {
3183  sPrevProjectFilePath.clear();
3184 
3185  QFile projectFile( qgsProjectFile );
3186  if ( !projectFile.open( QIODevice::ReadOnly ) )
3187  {
3188  return false;
3189  }
3190 
3191  if ( !sProjectDocument.setContent( &projectFile ) )
3192  {
3193  return false;
3194  }
3195 
3196  sPrevProjectFilePath = projectFilePath;
3197  sPrevProjectFileTimestamp = projectFileTimestamp;
3198  }
3199 
3200  // does project store paths absolute or relative?
3201  bool useAbsolutePaths = true;
3202 
3203  const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
3204  if ( !propertiesElem.isNull() )
3205  {
3206  const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
3207  if ( !absElem.isNull() )
3208  {
3209  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
3210  }
3211  }
3212 
3213  QgsReadWriteContext embeddedContext;
3214  if ( !useAbsolutePaths )
3215  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
3216  embeddedContext.setProjectTranslator( this );
3217  embeddedContext.setTransformContext( transformContext() );
3218 
3219  const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
3220  if ( projectLayersElem.isNull() )
3221  {
3222  return false;
3223  }
3224 
3225  QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral( "maplayer" ) );
3226  while ( ! mapLayerElem.isNull() )
3227  {
3228  // get layer id
3229  const QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
3230  if ( id == layerId )
3231  {
3232  // layer can be embedded only once
3233  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
3234  {
3235  return false;
3236  }
3237 
3238  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
3239 
3240  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
3241  {
3242  return true;
3243  }
3244  else
3245  {
3246  mEmbeddedLayers.remove( layerId );
3247  return false;
3248  }
3249  }
3250  mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
3251  }
3252 
3253  return false;
3254 }
3255 
3256 
3257 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags )
3258 {
3259  QString qgsProjectFile = projectFilePath;
3260  QgsProjectArchive archive;
3261  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3262  {
3263  archive.unzip( projectFilePath );
3264  qgsProjectFile = archive.projectFile();
3265  }
3266 
3267  // open project file, get layer ids in group, add the layers
3268  QFile projectFile( qgsProjectFile );
3269  if ( !projectFile.open( QIODevice::ReadOnly ) )
3270  {
3271  return nullptr;
3272  }
3273 
3274  QDomDocument projectDocument;
3275  if ( !projectDocument.setContent( &projectFile ) )
3276  {
3277  return nullptr;
3278  }
3279 
3280  QgsReadWriteContext context;
3281  context.setPathResolver( pathResolver() );
3282  context.setProjectTranslator( this );
3284 
3286 
3287  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
3288  if ( !layerTreeElem.isNull() )
3289  {
3290  root->readChildrenFromXml( layerTreeElem, context );
3291  }
3292  else
3293  {
3294  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
3295  }
3296 
3297  QgsLayerTreeGroup *group = root->findGroup( groupName );
3298  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
3299  {
3300  // embedded groups cannot be embedded again
3301  delete root;
3302  return nullptr;
3303  }
3304 
3305  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
3306  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
3307  delete root;
3308  root = nullptr;
3309 
3310  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3311  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
3312 
3313  // set "embedded" to all children + load embedded layers
3314  mLayerTreeRegistryBridge->setEnabled( false );
3315  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
3316  mLayerTreeRegistryBridge->setEnabled( true );
3317 
3318  // consider the layers might be identify disabled in its project
3319  const auto constFindLayerIds = newGroup->findLayerIds();
3320  for ( const QString &layerId : constFindLayerIds )
3321  {
3322  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
3323  if ( layer )
3324  {
3325  layer->resolveReferences( this );
3326  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
3327  }
3328  }
3329 
3330  return newGroup;
3331 }
3332 
3333 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
3334 {
3335  const auto constChildren = group->children();
3336  for ( QgsLayerTreeNode *child : constChildren )
3337  {
3338  // all nodes in the subtree will have "embedded" custom property set
3339  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3340 
3341  if ( QgsLayerTree::isGroup( child ) )
3342  {
3343  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
3344  }
3345  else if ( QgsLayerTree::isLayer( child ) )
3346  {
3347  // load the layer into our project
3348  QList<QDomNode> brokenNodes;
3349  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
3350  }
3351  }
3352 }
3353 
3355 {
3357 }
3358 
3359 void QgsProject::setEvaluateDefaultValues( bool evaluateDefaultValues )
3360 {
3362 }
3363 
3365 {
3366  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
3368 }
3369 
3371 {
3372  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
3373 }
3374 
3376 {
3377  const QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
3378  if ( !distanceUnitString.isEmpty() )
3379  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
3380 
3381  //fallback to QGIS default measurement unit
3382  bool ok = false;
3383  const QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
3384  return ok ? type : QgsUnitTypes::DistanceMeters;
3385 }
3386 
3388 {
3389  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
3390 }
3391 
3393 {
3394  const QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
3395  if ( !areaUnitString.isEmpty() )
3396  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
3397 
3398  //fallback to QGIS default area unit
3399  bool ok = false;
3400  const QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
3401  return ok ? type : QgsUnitTypes::AreaSquareMeters;
3402 }
3403 
3405 {
3406  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
3407 }
3408 
3409 QString QgsProject::homePath() const
3410 {
3411  if ( !mCachedHomePath.isEmpty() )
3412  return mCachedHomePath;
3413 
3414  const QFileInfo pfi( fileName() );
3415 
3416  if ( !mHomePath.isEmpty() )
3417  {
3418  const QFileInfo homeInfo( mHomePath );
3419  if ( !homeInfo.isRelative() )
3420  {
3421  mCachedHomePath = mHomePath;
3422  return mHomePath;
3423  }
3424  }
3425  else if ( !fileName().isEmpty() )
3426  {
3427  mCachedHomePath = pfi.path();
3428 
3429  return mCachedHomePath;
3430  }
3431 
3432  if ( !pfi.exists() )
3433  {
3434  mCachedHomePath = mHomePath;
3435  return mHomePath;
3436  }
3437 
3438  if ( !mHomePath.isEmpty() )
3439  {
3440  // path is relative to project file
3441  mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
3442  }
3443  else
3444  {
3445  mCachedHomePath = pfi.canonicalPath();
3446  }
3447  return mCachedHomePath;
3448 }
3449 
3451 {
3452  return mHomePath;
3453 }
3454 
3456 {
3457  return mRelationManager;
3458 }
3459 
3461 {
3462  return mLayoutManager.get();
3463 }
3464 
3466 {
3467  return mLayoutManager.get();
3468 }
3469 
3471 {
3472  return m3DViewsManager.get();
3473 }
3474 
3476 {
3477  return m3DViewsManager.get();
3478 }
3479 
3481 {
3482  return mBookmarkManager;
3483 }
3484 
3486 {
3487  return mBookmarkManager;
3488 }
3489 
3491 {
3492  return mViewSettings;
3493 }
3494 
3496 {
3497  return mViewSettings;
3498 }
3499 
3501 {
3502  return mStyleSettings;
3503 }
3504 
3506 {
3507  return mStyleSettings;
3508 }
3509 
3511 {
3512  return mTimeSettings;
3513 }
3514 
3516 {
3517  return mTimeSettings;
3518 }
3519 
3521 {
3522  return mElevationProperties;
3523 }
3524 
3526 {
3527  return mElevationProperties;
3528 }
3529 
3531 {
3532  return mDisplaySettings;
3533 }
3534 
3536 {
3537  return mDisplaySettings;
3538 }
3539 
3541 {
3542  return mRootGroup;
3543 }
3544 
3546 {
3547  return mMapThemeCollection.get();
3548 }
3549 
3551 {
3552  return mAnnotationManager.get();
3553 }
3554 
3556 {
3557  return mAnnotationManager.get();
3558 }
3559 
3560 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
3561 {
3562  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3563  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3564  {
3565  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3566  continue;
3567 
3568  if ( layers.contains( it.value() ) )
3569  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
3570  else
3571  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
3572  }
3573 
3577 }
3578 
3579 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
3580 {
3581  QList<QgsMapLayer *> nonIdentifiableLayers;
3582  nonIdentifiableLayers.reserve( layerIds.count() );
3583  for ( const QString &layerId : layerIds )
3584  {
3585  QgsMapLayer *layer = mapLayer( layerId );
3586  if ( layer )
3587  nonIdentifiableLayers << layer;
3588  }
3592 }
3593 
3595 {
3596  QStringList nonIdentifiableLayers;
3597 
3598  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3599  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3600  {
3601  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3602  {
3603  nonIdentifiableLayers.append( it.value()->id() );
3604  }
3605  }
3606  return nonIdentifiableLayers;
3607 }
3608 
3610 {
3611  return mTransactionMode == Qgis::TransactionMode::AutomaticGroups;
3612 }
3613 
3614 void QgsProject::setAutoTransaction( bool autoTransaction )
3615 {
3616  if ( autoTransaction
3617  && mTransactionMode == Qgis::TransactionMode::AutomaticGroups )
3618  return;
3619 
3620  if ( ! autoTransaction
3621  && mTransactionMode == Qgis::TransactionMode::Disabled )
3622  return;
3623 
3624  if ( autoTransaction )
3625  mTransactionMode = Qgis::TransactionMode::AutomaticGroups;
3626  else
3627  mTransactionMode = Qgis::TransactionMode::Disabled;
3628 
3629  updateTransactionGroups();
3630 }
3631 
3633 {
3634  return mTransactionMode;
3635 }
3636 
3638 {
3639  if ( transactionMode == mTransactionMode )
3640  return true;
3641 
3642  // Check that all layer are not in edit mode
3643  const auto constLayers = mapLayers().values();
3644  for ( QgsMapLayer *layer : constLayers )
3645  {
3646  if ( layer->isEditable() )
3647  {
3648  QgsLogger::warning( tr( "Transaction mode can be changed only if all layers are not editable." ) );
3649  return false;
3650  }
3651  }
3652 
3653  mTransactionMode = transactionMode;
3654  updateTransactionGroups();
3655  return true;
3656 }
3657 
3658 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
3659 {
3660  return mTransactionGroups;
3661 }
3662 
3663 
3664 //
3665 // QgsMapLayerStore methods
3666 //
3667 
3668 
3670 {
3671  return mLayerStore->count();
3672 }
3673 
3675 {
3676  return mLayerStore->validCount();
3677 }
3678 
3679 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
3680 {
3681  return mLayerStore->mapLayer( layerId );
3682 }
3683 
3684 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
3685 {
3686  return mLayerStore->mapLayersByName( layerName );
3687 }
3688 
3689 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
3690 {
3691  QList<QgsMapLayer *> layers;
3692  const auto constMapLayers { mLayerStore->mapLayers() };
3693  for ( const auto &l : constMapLayers )
3694  {
3695  if ( ! l->shortName().isEmpty() )
3696  {
3697  if ( l->shortName() == shortName )
3698  layers << l;
3699  }
3700  else if ( l->name() == shortName )
3701  {
3702  layers << l;
3703  }
3704  }
3705  return layers;
3706 }
3707 
3708 bool QgsProject::unzip( const QString &filename, Qgis::ProjectReadFlags flags )
3709 {
3710  clearError();
3711  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3712 
3713  // unzip the archive
3714  if ( !archive->unzip( filename ) )
3715  {
3716  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
3717  return false;
3718  }
3719 
3720  // test if zip provides a .qgs file
3721  if ( archive->projectFile().isEmpty() )
3722  {
3723  setError( tr( "Zip archive does not provide a project file" ) );
3724  return false;
3725  }
3726 
3727  // Keep the archive
3728  mArchive = std::move( archive );
3729 
3730  // load auxiliary storage
3731  if ( !static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile().isEmpty() )
3732  {
3733  // database file is already a copy as it's been unzipped. So we don't open
3734  // auxiliary storage in copy mode in this case
3735  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile(), false ) );
3736  }
3737  else
3738  {
3739  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
3740  }
3741 
3742  // read the project file
3743  if ( ! readProjectFile( static_cast<QgsProjectArchive *>( mArchive.get() )->projectFile(), flags ) )
3744  {
3745  setError( tr( "Cannot read unzipped qgs project file" ) );
3746  return false;
3747  }
3748 
3749  // Remove the temporary .qgs file
3750  static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3751 
3752  return true;
3753 }
3754 
3755 bool QgsProject::zip( const QString &filename )
3756 {
3757  clearError();
3758 
3759  // save the current project in a temporary .qgs file
3760  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3761  const QString baseName = QFileInfo( filename ).baseName();
3762  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
3763  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
3764 
3765  bool writeOk = false;
3766  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3767  {
3768  writeOk = writeProjectFile( qgsFile.fileName() );
3769  qgsFile.close();
3770  }
3771 
3772  // stop here with an error message
3773  if ( ! writeOk )
3774  {
3775  setError( tr( "Unable to write temporary qgs file" ) );
3776  return false;
3777  }
3778 
3779  // save auxiliary storage
3780  const QFileInfo info( qgsFile );
3781  const QString asExt = QStringLiteral( ".%1" ).arg( QgsAuxiliaryStorage::extension() );
3782  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
3783 
3784  bool auxiliaryStorageSavedOk = true;
3785  if ( ! saveAuxiliaryStorage( asFileName ) )
3786  {
3787  const QString err = mAuxiliaryStorage->errorString();
3788  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 ) );
3789  auxiliaryStorageSavedOk = false;
3790 
3791  // fixes the current archive and keep the previous version of qgd
3792  if ( !mArchive->exists() )
3793  {
3794  mArchive.reset( new QgsProjectArchive() );
3795  mArchive->unzip( mFile.fileName() );
3796  static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3797 
3798  const QString auxiliaryStorageFile = static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile();
3799  if ( ! auxiliaryStorageFile.isEmpty() )
3800  {
3801  archive->addFile( auxiliaryStorageFile );
3802  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( auxiliaryStorageFile, false ) );
3803  }
3804  }
3805  }
3806  else
3807  {
3808  // in this case, an empty filename means that the auxiliary database is
3809  // empty, so we don't want to save it
3810  if ( QFile::exists( asFileName ) )
3811  {
3812  archive->addFile( asFileName );
3813  }
3814  }
3815 
3816  // create the archive
3817  archive->addFile( qgsFile.fileName() );
3818 
3819  // Add all other files
3820  const QStringList &files = mArchive->files();
3821  for ( const QString &file : files )
3822  {
3823  if ( !file.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
3824  {
3825  archive->addFile( file );
3826  }
3827  }
3828 
3829  // zip
3830  bool zipOk = true;
3831  if ( !archive->zip( filename ) )
3832  {
3833  setError( tr( "Unable to perform zip" ) );
3834  zipOk = false;
3835  }
3836 
3837  return auxiliaryStorageSavedOk && zipOk;
3838 }
3839 
3841 {
3842  return QgsZipUtils::isZipFile( mFile.fileName() );
3843 }
3844 
3845 QList<QgsMapLayer *> QgsProject::addMapLayers(
3846  const QList<QgsMapLayer *> &layers,
3847  bool addToLegend,
3848  bool takeOwnership )
3849 {
3850  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
3851  if ( !myResultList.isEmpty() )
3852  {
3853  // Update transform context
3854  for ( auto &l : myResultList )
3855  {
3856  l->setTransformContext( transformContext() );
3857  }
3858  if ( addToLegend )
3859  {
3860  emit legendLayersAdded( myResultList );
3861  }
3862  }
3863 
3864  if ( mAuxiliaryStorage )
3865  {
3866  for ( QgsMapLayer *mlayer : myResultList )
3867  {
3868  if ( mlayer->type() != QgsMapLayerType::VectorLayer )
3869  continue;
3870 
3871  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
3872  if ( vl )
3873  {
3874  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
3875  }
3876  }
3877  }
3878 
3879  mProjectScope.reset();
3880 
3881  return myResultList;
3882 }
3883 
3884 QgsMapLayer *
3886  bool addToLegend,
3887  bool takeOwnership )
3888 {
3889  QList<QgsMapLayer *> addedLayers;
3890  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
3891  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
3892 }
3893 
3894 void QgsProject::removeMapLayers( const QStringList &layerIds )
3895 {
3896  mProjectScope.reset();
3897  mLayerStore->removeMapLayers( layerIds );
3898 }
3899 
3900 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3901 {
3902  mProjectScope.reset();
3903  mLayerStore->removeMapLayers( layers );
3904 }
3905 
3906 void QgsProject::removeMapLayer( const QString &layerId )
3907 {
3908  mProjectScope.reset();
3909  mLayerStore->removeMapLayer( layerId );
3910 }
3911 
3913 {
3914  mProjectScope.reset();
3915  mLayerStore->removeMapLayer( layer );
3916 }
3917 
3919 {
3920  mProjectScope.reset();
3921  return mLayerStore->takeMapLayer( layer );
3922 }
3923 
3925 {
3926  return mMainAnnotationLayer;
3927 }
3928 
3930 {
3931  if ( mLayerStore->count() == 0 )
3932  return;
3933 
3934  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
3935  mProjectScope.reset();
3936  mLayerStore->removeAllMapLayers();
3937 
3938  snapSingleBlocker.release();
3939  mSnappingConfig.clearIndividualLayerSettings();
3940  if ( !mBlockSnappingUpdates )
3941  emit snappingConfigChanged( mSnappingConfig );
3942 }
3943 
3945 {
3946  const QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3947  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3948  for ( ; it != layers.constEnd(); ++it )
3949  {
3950  it.value()->reload();
3951  }
3952 }
3953 
3954 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3955 {
3956  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3957 }
3958 
3959 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3960 {
3961  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3962 }
3963 
3965 {
3966  return &mEditBufferGroup;
3967 }
3968 
3970 {
3971  QgsCoordinateReferenceSystem defaultCrs;
3972 
3973  // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
3974  // 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)
3975  if ( mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
3976  || mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == QLatin1String( "2" ) )
3977  {
3978  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
3979  defaultCrs = crs();
3980  }
3981  else
3982  {
3983  // global crs
3984  const QString layerDefaultCrs = mSettings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
3985  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
3986  }
3987 
3988  return defaultCrs;
3989 }
3990 
3992 {
3994 }
3995 
3997 {
3999 }
4000 
4001 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
4002 {
4003  const QMap<QString, QgsMapLayer *> layers = mapLayers();
4004  bool empty = true;
4005  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4006  {
4007  if ( it.value()->type() != QgsMapLayerType::VectorLayer )
4008  continue;
4009 
4010  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
4011  if ( vl && vl->auxiliaryLayer() )
4012  {
4013  vl->auxiliaryLayer()->save();
4014  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
4015  }
4016  }
4017 
4018  if ( !mAuxiliaryStorage->exists( *this ) && empty )
4019  {
4020  return true; // it's not an error
4021  }
4022  else if ( !filename.isEmpty() )
4023  {
4024  return mAuxiliaryStorage->saveAs( filename );
4025  }
4026  else
4027  {
4028  return mAuxiliaryStorage->saveAs( *this );
4029  }
4030 }
4031 
4032 QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
4033 {
4034  static QgsPropertiesDefinition sPropertyDefinitions
4035  {
4036  {
4037  QgsProject::DataDefinedServerProperty::WMSOnlineResource,
4038  QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
4039  },
4040  };
4041  return sPropertyDefinitions;
4042 }
4043 
4045 {
4046  return mAuxiliaryStorage.get();
4047 }
4048 
4050 {
4051  return mAuxiliaryStorage.get();
4052 }
4053 
4054 QString QgsProject::createAttachedFile( const QString &nameTemplate )
4055 {
4056  const QDir archiveDir( mArchive->dir() );
4057  QTemporaryFile tmpFile( archiveDir.filePath( "XXXXXX_" + nameTemplate ), this );
4058  tmpFile.setAutoRemove( false );
4059  tmpFile.open();
4060  mArchive->addFile( tmpFile.fileName() );
4061  return tmpFile.fileName();
4062 }
4063 
4064 QStringList QgsProject::attachedFiles() const
4065 {
4066  QStringList attachments;
4067  const QString baseName = QFileInfo( fileName() ).baseName();
4068  const QStringList files = mArchive->files();
4069  attachments.reserve( files.size() );
4070  for ( const QString &file : files )
4071  {
4072  if ( QFileInfo( file ).baseName() != baseName )
4073  {
4074  attachments.append( file );
4075  }
4076  }
4077  return attachments;
4078 }
4079 
4080 bool QgsProject::removeAttachedFile( const QString &path )
4081 {
4082  return mArchive->removeFile( path );
4083 }
4084 
4085 QString QgsProject::attachmentIdentifier( const QString &attachedFile ) const
4086 {
4087  return QStringLiteral( "attachment:///%1" ).arg( QFileInfo( attachedFile ).fileName() );
4088 }
4089 
4090 QString QgsProject::resolveAttachmentIdentifier( const QString &identifier ) const
4091 {
4092  if ( identifier.startsWith( QLatin1String( "attachment:///" ) ) )
4093  {
4094  return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
4095  }
4096  return QString();
4097 }
4098 
4100 {
4101  return mMetadata;
4102 }
4103 
4105 {
4106  if ( metadata == mMetadata )
4107  return;
4108 
4109  mMetadata = metadata;
4110  mProjectScope.reset();
4111 
4112  emit metadataChanged();
4113 
4114  setDirty( true );
4115 }
4116 
4117 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
4118 {
4119  QSet<QgsMapLayer *> requiredLayers;
4120 
4121  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4122  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4123  {
4124  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4125  {
4126  requiredLayers.insert( it.value() );
4127  }
4128  }
4129  return requiredLayers;
4130 }
4131 
4132 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
4133 {
4134  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4135  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4136  {
4137  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4138  continue;
4139 
4140  if ( layers.contains( it.value() ) )
4141  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
4142  else
4143  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
4144  }
4145 }
4146 
4148 {
4149  // save colors to project
4150  QStringList customColors;
4151  QStringList customColorLabels;
4152 
4153  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
4154  for ( ; colorIt != colors.constEnd(); ++colorIt )
4155  {
4156  const QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
4157  const QString label = ( *colorIt ).second;
4158  customColors.append( color );
4159  customColorLabels.append( label );
4160  }
4161  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
4162  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
4163  mProjectScope.reset();
4164  emit projectColorsChanged();
4165 }
4166 
4167 void QgsProject::setBackgroundColor( const QColor &color )
4168 {
4169  if ( mBackgroundColor == color )
4170  return;
4171 
4172  mBackgroundColor = color;
4173  emit backgroundColorChanged();
4174 }
4175 
4177 {
4178  return mBackgroundColor;
4179 }
4180 
4181 void QgsProject::setSelectionColor( const QColor &color )
4182 {
4183  if ( mSelectionColor == color )
4184  return;
4185 
4186  mSelectionColor = color;
4187  emit selectionColorChanged();
4188 }
4189 
4191 {
4192  return mSelectionColor;
4193 }
4194 
4195 void QgsProject::setMapScales( const QVector<double> &scales )
4196 {
4197  mViewSettings->setMapScales( scales );
4198 }
4199 
4200 QVector<double> QgsProject::mapScales() const
4201 {
4202  return mViewSettings->mapScales();
4203 }
4204 
4206 {
4207  mViewSettings->setUseProjectScales( enabled );
4208 }
4209 
4211 {
4212  return mViewSettings->useProjectScales();
4213 }
4214 
4215 void QgsProject::generateTsFile( const QString &locale )
4216 {
4217  QgsTranslationContext translationContext;
4218  translationContext.setProject( this );
4219  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
4220 
4221  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
4222 
4223  translationContext.writeTsFile( locale );
4224 }
4225 
4226 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
4227 {
4228  if ( !mTranslator )
4229  {
4230  return sourceText;
4231  }
4232 
4233  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
4234 
4235  if ( result.isEmpty() )
4236  {
4237  return sourceText;
4238  }
4239  return result;
4240 }
4241 
4243 {
4244  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
4245  if ( !layers.empty() )
4246  {
4247  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4248  {
4249  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
4250  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4251  {
4252  if ( !( ( *it )->accept( visitor ) ) )
4253  return false;
4254 
4255  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4256  return false;
4257  }
4258  }
4259  }
4260 
4261  if ( !mLayoutManager->accept( visitor ) )
4262  return false;
4263 
4264  if ( !mAnnotationManager->accept( visitor ) )
4265  return false;
4266 
4267  return true;
4268 }
4269 
4271 GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
4272  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
4273 {
4274  if ( !project )
4275  return;
4276 
4277  //build up color list from project. Do this in advance for speed
4278  QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
4279  const QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
4280 
4281  //generate list from custom colors
4282  int colorIndex = 0;
4283  for ( QStringList::iterator it = colorStrings.begin();
4284  it != colorStrings.end(); ++it )
4285  {
4286  const QColor color = QgsSymbolLayerUtils::decodeColor( *it );
4287  QString label;
4288  if ( colorLabels.length() > colorIndex )
4289  {
4290  label = colorLabels.at( colorIndex );
4291  }
4292 
4293  mColors.insert( label.toLower(), color );
4294  colorIndex++;
4295  }
4296 }
4297 
4298 GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
4299  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
4300  , mColors( colors )
4301 {
4302 }
4303 
4304 QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
4305 {
4306  const QString colorName = values.at( 0 ).toString().toLower();
4307  if ( mColors.contains( colorName ) )
4308  {
4309  return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
4310  }
4311  else
4312  return QVariant();
4313 }
4314 
4315 QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
4316 {
4317  return new GetNamedProjectColor( mColors );
4318 }
static QString version()
Version string.
Definition: qgis.cpp:277
FilePathType
File path types.
Definition: qgis.h:752
@ Relative
Relative path.
@ Absolute
Absolute path.
TransactionMode
Transaction mode.
Definition: qgis.h:1763
@ 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.
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
Definition: qgis.h:1934
@ 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:1827
@ 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...
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
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 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.
static const QgsSettingsEntryString settingsLocaleUserLocale
Settings entry locale user locale.
Class allowing to manage the zip/unzip actions.
Definition: qgsarchive.h:36
void addFile(const QString &filename)
Add a new file to this archive.
Definition: qgsarchive.cpp:107
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.
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.
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.
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.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
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.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
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.
@ EvaluateDefaultValues
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
QVariantMap config() const
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...
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:51
QString name
Definition: qgsfield.h:60
QString alias
Definition: qgsfield.h:61
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:602
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 childrens, disconnect all the forwarded and external signals and sets their parent to nul...
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:33
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:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
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:64
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:122
static QgsMapLayerType 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:73
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.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
void configChanged()
Emitted whenever the configuration is changed.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
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 ...
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 readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
bool isValid
Definition: qgsmaplayer.h:81
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:145
@ Removable
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
Definition: qgsmaplayer.h:146
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:641
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:640
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
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:122
QString projectFile() const
Returns the current .qgs project file or an empty string if there's none.
Definition: qgsarchive.cpp:134
QString auxiliaryStorageFile() const
Returns the current .qgd auxiliary storage file or an empty string if there's none.
Definition: qgsarchive.cpp:160
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Definition: qgsarchive.cpp:147
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.
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 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:104
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
QgsProject(QObject *parent=nullptr)
Create a new QgsProject.
Definition: qgsproject.cpp:369
bool removeAttachedFile(const QString &path)
Removes the attached file.
QgsRelationManager * relationManager
Definition: qgsproject.h:114
bool write()
Writes the project to its current associated file (see fileName() ).
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.
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the default area measurement units for the project.
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.
Definition: qgsproject.cpp:931
~QgsProject() override
Definition: qgsproject.cpp:458
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:188
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project's associated file.
Definition: qgsproject.cpp:787
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:871
QColor selectionColor
Definition: qgsproject.h:119
QString title() const
Returns the project's title.
Definition: qgsproject.cpp:502
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:704
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:479
Qgis::FilePathType filePathStorage() const
Returns the type of paths used when storing file paths in a QGS/QGZ project file.
Definition: qgsproject.cpp:847
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:111
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.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the default distance measurement units for the project.
QgsPropertyCollection dataDefinedServerProperties() const
Returns the data defined properties used for overrides in user defined server parameters.
Definition: qgsproject.cpp:680
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:728
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.
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:113
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:752
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
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:675
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
Definition: qgsproject.cpp:622
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:685
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:1164
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:811
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:116
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:833
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:112
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.
Qgis::TransactionMode transactionMode() const
Returns the transaction mode.
QgsProjectMetadata metadata
Definition: qgsproject.h:117
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
QgsUnitTypes::DistanceUnit distanceUnits() const
Convenience function to query default distance measurement units for project.
QString saveUser() const
Returns the user name that did the last save.
Definition: qgsproject.cpp:550
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:110
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:782
void setOriginalPath(const QString &path)
Sets the original path associated with the project.
Definition: qgsproject.cpp:777
void dumpProperties() const
Dump out current project properties to stderr.
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:473
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...
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QString fileName
Definition: qgsproject.h:107
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:109
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:106
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 setTitle(const QString &title)
Sets the project's title.
Definition: qgsproject.cpp:490
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:540
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:797
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:822
QDateTime lastSaveDateTime() const
Returns the date and time when the project was last saved.
Definition: qgsproject.cpp:560
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:900
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:915
QColor backgroundColor
Definition: qgsproject.h:118
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:120
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:575
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:604
void setFilePathStorage(Qgis::FilePathType type)
Sets the type of paths used when storing file paths in a QGS/QGZ project file.
Definition: qgsproject.cpp:853
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.
const QgsProjectDisplaySettings * displaySettings() const
Returns the project's display settings, which settings and properties relating to how a QgsProject sh...
QString saveUserFullName() const
Returns the full user name that did the last save.
Definition: qgsproject.cpp:555
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:108
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:570
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
void isDirtyChanged(bool dirty)
Emitted when the project dirty status changes.
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:590
void setFlags(Qgis::ProjectFlags flags)
Sets the project's flags, which dictate the behavior of the project.
Definition: qgsproject.cpp:507
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:792
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:565
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:47
@ String
Any string value.
Definition: qgsproperty.h:61
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.
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.
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.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
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.
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
void triggerIconRebuild()
Triggers emission of the rebuildIconPreviews() signal.
Definition: qgsstyle.cpp:3054
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
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 setFileName(const QString &fileName)
Sets the fileName of the TS file.
void writeTsFile(const QString &locale) const
Writes the Ts-file.
void setProject(QgsProject *project)
Sets the project being translated.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE QgsUnitTypes::AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
AreaUnit
Units of area.
Definition: qgsunittypes.h:94
@ AreaSquareMeters
Square meters.
Definition: qgsunittypes.h:95
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.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
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
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:2733
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:2454
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2815
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:2435
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition: qgis.h:2493
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:2515
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2814
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:2727
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugCall
Definition: qgslogger.h:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2146
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:105
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
const QgsCoordinateReferenceSystem & crs
const QString & typeName
Setting options for loading annotation layers.
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.