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