QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
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"
64#include "qgsvectortilelayer.h"
65#include "qgsruntimeprofiler.h"
66#include "qgsannotationlayer.h"
67#include "qgspointcloudlayer.h"
69#include "qgsgrouplayer.h"
70#include "qgsmapviewsmanager.h"
73
74#include <algorithm>
75#include <QApplication>
76#include <QFileInfo>
77#include <QDomNode>
78#include <QObject>
79#include <QTextStream>
80#include <QTemporaryFile>
81#include <QDir>
82#include <QUrl>
83#include <QStandardPaths>
84#include <QUuid>
85#include <QRegularExpression>
86
87#ifdef _MSC_VER
88#include <sys/utime.h>
89#else
90#include <utime.h>
91#endif
92
93// canonical project instance
94QgsProject *QgsProject::sProject = nullptr;
95
104QStringList makeKeyTokens_( const QString &scope, const QString &key )
105{
106 QStringList keyTokens = QStringList( scope );
107#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
108 keyTokens += key.split( '/', QString::SkipEmptyParts );
109#else
110 keyTokens += key.split( '/', Qt::SkipEmptyParts );
111#endif
112
113 // be sure to include the canonical root node
114 keyTokens.push_front( QStringLiteral( "properties" ) );
115
116 //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.
117 for ( int i = 0; i < keyTokens.size(); ++i )
118 {
119 const QString keyToken = keyTokens.at( i );
120
121 //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
122 //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
123 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}])" ) );
124 if ( keyToken.contains( sInvalidRegexp ) )
125 {
126 const QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
128 }
129 }
130
131 return keyTokens;
132}
133
134
135
145QgsProjectProperty *findKey_( const QString &scope,
146 const QString &key,
147 QgsProjectPropertyKey &rootProperty )
148{
149 QgsProjectPropertyKey *currentProperty = &rootProperty;
150 QgsProjectProperty *nextProperty; // link to next property down hierarchy
151
152 QStringList keySequence = makeKeyTokens_( scope, key );
153
154 while ( !keySequence.isEmpty() )
155 {
156 // if the current head of the sequence list matches the property name,
157 // then traverse down the property hierarchy
158 if ( keySequence.first() == currentProperty->name() )
159 {
160 // remove front key since we're traversing down a level
161 keySequence.pop_front();
162
163 if ( 1 == keySequence.count() )
164 {
165 // if we have only one key name left, then return the key found
166 return currentProperty->find( keySequence.front() );
167 }
168 else if ( keySequence.isEmpty() )
169 {
170 // if we're out of keys then the current property is the one we
171 // want; i.e., we're in the rate case of being at the top-most
172 // property node
173 return currentProperty;
174 }
175 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
176 {
177 if ( nextProperty->isKey() )
178 {
179 currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
180 }
181 else if ( nextProperty->isValue() && 1 == keySequence.count() )
182 {
183 // it may be that this may be one of several property value
184 // nodes keyed by QDict string; if this is the last remaining
185 // key token and the next property is a value node, then
186 // that's the situation, so return the currentProperty
187 return currentProperty;
188 }
189 else
190 {
191 // QgsProjectPropertyValue not Key, so return null
192 return nullptr;
193 }
194 }
195 else
196 {
197 // if the next key down isn't found
198 // then the overall key sequence doesn't exist
199 return nullptr;
200 }
201 }
202 else
203 {
204 return nullptr;
205 }
206 }
207
208 return nullptr;
209}
210
211
212
222QgsProjectProperty *addKey_( const QString &scope,
223 const QString &key,
224 QgsProjectPropertyKey *rootProperty,
225 const QVariant &value,
226 bool &propertiesModified )
227{
228 QStringList keySequence = makeKeyTokens_( scope, key );
229
230 // cursor through property key/value hierarchy
231 QgsProjectPropertyKey *currentProperty = rootProperty;
232 QgsProjectProperty *nextProperty; // link to next property down hierarchy
233 QgsProjectPropertyKey *newPropertyKey = nullptr;
234
235 propertiesModified = false;
236 while ( ! keySequence.isEmpty() )
237 {
238 // if the current head of the sequence list matches the property name,
239 // then traverse down the property hierarchy
240 if ( keySequence.first() == currentProperty->name() )
241 {
242 // remove front key since we're traversing down a level
243 keySequence.pop_front();
244
245 // if key sequence has one last element, then we use that as the
246 // name to store the value
247 if ( 1 == keySequence.count() )
248 {
249 QgsProjectProperty *property = currentProperty->find( keySequence.front() );
250 if ( !property || property->value() != value )
251 {
252 currentProperty->setValue( keySequence.front(), value );
253 propertiesModified = true;
254 }
255
256 return currentProperty;
257 }
258 // we're at the top element if popping the keySequence element
259 // will leave it empty; in that case, just add the key
260 else if ( keySequence.isEmpty() )
261 {
262 if ( currentProperty->value() != value )
263 {
264 currentProperty->setValue( value );
265 propertiesModified = true;
266 }
267
268 return currentProperty;
269 }
270 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
271 {
272 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
273
274 if ( currentProperty )
275 {
276 continue;
277 }
278 else // QgsProjectPropertyValue not Key, so return null
279 {
280 return nullptr;
281 }
282 }
283 else // the next subkey doesn't exist, so add it
284 {
285 if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
286 {
287 currentProperty = newPropertyKey;
288 }
289 continue;
290 }
291 }
292 else
293 {
294 return nullptr;
295 }
296 }
297
298 return nullptr;
299}
300
308void removeKey_( const QString &scope,
309 const QString &key,
310 QgsProjectPropertyKey &rootProperty )
311{
312 QgsProjectPropertyKey *currentProperty = &rootProperty;
313
314 QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
315 QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
316
317 QStringList keySequence = makeKeyTokens_( scope, key );
318
319 while ( ! keySequence.isEmpty() )
320 {
321 // if the current head of the sequence list matches the property name,
322 // then traverse down the property hierarchy
323 if ( keySequence.first() == currentProperty->name() )
324 {
325 // remove front key since we're traversing down a level
326 keySequence.pop_front();
327
328 // if we have only one key name left, then try to remove the key
329 // with that name
330 if ( 1 == keySequence.count() )
331 {
332 currentProperty->removeKey( keySequence.front() );
333 }
334 // if we're out of keys then the current property is the one we
335 // want to remove, but we can't delete it directly; we need to
336 // delete it from the parent property key container
337 else if ( keySequence.isEmpty() )
338 {
339 previousQgsPropertyKey->removeKey( currentProperty->name() );
340 }
341 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
342 {
343 previousQgsPropertyKey = currentProperty;
344 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
345
346 if ( currentProperty )
347 {
348 continue;
349 }
350 else // QgsProjectPropertyValue not Key, so return null
351 {
352 return;
353 }
354 }
355 else // if the next key down isn't found
356 {
357 // then the overall key sequence doesn't exist
358 return;
359 }
360 }
361 else
362 {
363 return;
364 }
365 }
366}
367
368QgsProject::QgsProject( QObject *parent, Qgis::ProjectCapabilities capabilities )
369 : QObject( parent )
370 , mCapabilities( capabilities )
371 , mLayerStore( new QgsMapLayerStore( this ) )
372 , mBadLayerHandler( new QgsProjectBadLayerHandler() )
373 , mSnappingConfig( this )
374 , mRelationManager( new QgsRelationManager( this ) )
375 , mAnnotationManager( new QgsAnnotationManager( this ) )
376 , mLayoutManager( new QgsLayoutManager( this ) )
377 , m3DViewsManager( new QgsMapViewsManager( this ) )
378 , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
379 , mViewSettings( new QgsProjectViewSettings( this ) )
380 , mStyleSettings( new QgsProjectStyleSettings( this ) )
381 , mTimeSettings( new QgsProjectTimeSettings( this ) )
382 , mElevationProperties( new QgsProjectElevationProperties( this ) )
383 , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
384 , mRootGroup( new QgsLayerTree )
385 , mLabelingEngineSettings( new QgsLabelingEngineSettings )
386 , mArchive( new QgsArchive() )
387 , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
388{
389 mProperties.setName( QStringLiteral( "properties" ) );
390
391 mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
392 mMainAnnotationLayer->setParent( this );
393
394 clear();
395
396 // bind the layer tree to the map layer registry.
397 // whenever layers are added to or removed from the registry,
398 // layer tree will be updated
399 mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
400 connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
401 connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
402 connect( this, qOverload< const QList<QgsMapLayer *> & >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
403
404 // proxy map layer store signals to this
405 connect( mLayerStore.get(), qOverload<const QStringList &>( &QgsMapLayerStore::layersWillBeRemoved ),
406 this, [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
407 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ),
408 this, [ = ]( const QList<QgsMapLayer *> &layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
409 connect( mLayerStore.get(), qOverload< const QString & >( &QgsMapLayerStore::layerWillBeRemoved ),
410 this, [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
411 connect( mLayerStore.get(), qOverload< QgsMapLayer * >( &QgsMapLayerStore::layerWillBeRemoved ),
412 this, [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
413 connect( mLayerStore.get(), qOverload<const QStringList & >( &QgsMapLayerStore::layersRemoved ), this,
414 [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
415 connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this,
416 [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
417 connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this,
418 [ = ]() { mProjectScope.reset(); emit removeAll(); } );
419 connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this,
420 [ = ]( const QList< QgsMapLayer * > &layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
421 connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this,
422 [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
423
425 {
427 }
428
429 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ), this,
430 [ = ]( const QList<QgsMapLayer *> &layers )
431 {
432 for ( const auto &layer : layers )
433 {
434 disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
435 }
436 }
437 );
438 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersAdded ), this,
439 [ = ]( const QList<QgsMapLayer *> &layers )
440 {
441 for ( const auto &layer : layers )
442 {
443 connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
444 }
445 }
446 );
447
451
452 mStyleSettings->combinedStyleModel()->addDefaultStyle();
453}
454
455
457{
458 mIsBeingDeleted = true;
459
460 clear();
461 delete mBadLayerHandler;
462 delete mRelationManager;
463 delete mLayerTreeRegistryBridge;
464 delete mRootGroup;
465 if ( this == sProject )
466 {
467 sProject = nullptr;
468 }
469}
470
472{
473 sProject = project;
474}
475
476
478{
479 if ( !sProject )
480 {
481 sProject = new QgsProject;
482
484 }
485 return sProject;
486}
487
488void QgsProject::setTitle( const QString &title )
489{
490 if ( title == mMetadata.title() )
491 return;
492
493 mMetadata.setTitle( title );
494 mProjectScope.reset();
495 emit metadataChanged();
496
497 setDirty( true );
498}
499
500QString QgsProject::title() const
501{
502 return mMetadata.title();
503}
504
505void QgsProject::setFlags( Qgis::ProjectFlags flags )
506{
507 const bool oldEvaluateDefaultValues = mFlags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
508 const bool newEvaluateDefaultValues = flags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
509 if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
510 {
511 const QMap<QString, QgsMapLayer *> layers = mapLayers();
512 for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
513 {
514 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
515 if ( vl->dataProvider() )
516 vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, newEvaluateDefaultValues );
517 }
518 }
519
520 const bool oldTrustLayerMetadata = mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
521 const bool newTrustLayerMetadata = flags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
522 if ( oldTrustLayerMetadata != newTrustLayerMetadata )
523 {
524 const QMap<QString, QgsMapLayer *> layers = mapLayers();
525 for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
526 {
527 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
528 {
529 vl->setReadExtentFromXml( newTrustLayerMetadata );
530 }
531 }
532 }
533
534 if ( mFlags != flags )
535 {
536 mFlags = flags;
537 setDirty( true );
538 }
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
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 //fallback to QGIS default measurement unit
963 bool ok = false;
964 const QgsUnitTypes::DistanceUnit distanceUnit = QgsUnitTypes::decodeDistanceUnit( mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
965 setDistanceUnits( ok ? distanceUnit : QgsUnitTypes::DistanceMeters );
966 ok = false;
967 const QgsUnitTypes::AreaUnit areaUnits = QgsUnitTypes::decodeAreaUnit( mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
969
970 mEmbeddedLayers.clear();
971 mRelationManager->clear();
972 mAnnotationManager->clear();
973 mLayoutManager->clear();
974 m3DViewsManager->clear();
975 mBookmarkManager->clear();
976 mViewSettings->reset();
977 mTimeSettings->reset();
978 mElevationProperties->reset();
979 mDisplaySettings->reset();
980 mSnappingConfig.reset();
981 mAvoidIntersectionsMode = Qgis::AvoidIntersectionsMode::AllowIntersections;
984
985 mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
987
988 mLabelingEngineSettings->clear();
989
990 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
991 mArchive.reset( new QgsArchive() );
992
993 // must happen after archive reset!
994 mStyleSettings->reset();
995
997
998 if ( !mIsBeingDeleted )
999 {
1000 // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
1001 emit projectColorsChanged();
1002 }
1003
1004 // reset some default project properties
1005 // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
1006 writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
1007 writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
1008
1009 const bool defaultRelativePaths = mSettings.value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true ).toBool();
1011
1012 int red = mSettings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
1013 int green = mSettings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
1014 int blue = mSettings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
1015 setBackgroundColor( QColor( red, green, blue ) );
1016
1017 red = mSettings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
1018 green = mSettings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
1019 blue = mSettings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
1020 const int alpha = mSettings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
1021 setSelectionColor( QColor( red, green, blue, alpha ) );
1022
1023 mSnappingConfig.clearIndividualLayerSettings();
1024
1026 mRootGroup->clear();
1027 if ( mMainAnnotationLayer )
1028 mMainAnnotationLayer->reset();
1029
1030 snapSingleBlocker.release();
1031
1032 if ( !mBlockSnappingUpdates )
1033 emit snappingConfigChanged( mSnappingConfig );
1034
1035 setDirty( false );
1036 emit homePathChanged();
1037 emit cleared();
1038}
1039
1040// basically a debugging tool to dump property list values
1041void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
1042{
1043 QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
1044 topQgsPropertyKey.dump();
1045}
1046
1075void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
1076{
1077 const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1078
1079 if ( propertiesElem.isNull() ) // no properties found, so we're done
1080 {
1081 return;
1082 }
1083
1084 const QDomNodeList scopes = propertiesElem.childNodes();
1085
1086 if ( propertiesElem.firstChild().isNull() )
1087 {
1088 QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
1089 return;
1090 }
1091
1092 if ( ! project_properties.readXml( propertiesElem ) )
1093 {
1094 QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
1095 }
1096}
1097
1104QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
1105{
1106 QgsPropertyCollection ddServerProperties;
1107 // Read data defined server properties
1108 const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral( "dataDefinedServerProperties" ) );
1109 if ( !ddElem.isNull() )
1110 {
1111 if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1112 {
1113 QgsDebugMsg( QStringLiteral( "dataDefinedServerProperties.readXml() failed" ) );
1114 }
1115 }
1116 return ddServerProperties;
1117}
1118
1123static void _getTitle( const QDomDocument &doc, QString &title )
1124{
1125 const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral( "title" ) );
1126
1127 title.clear(); // by default the title will be empty
1128
1129 if ( titleNode.isNull() )
1130 {
1131 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1132 return;
1133 }
1134
1135 if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1136 {
1137 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1138 return;
1139 }
1140
1141 const QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1142
1143 if ( !titleTextNode.isText() )
1144 {
1145 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1146 return;
1147 }
1148
1149 const QDomText titleText = titleTextNode.toText();
1150
1151 title = titleText.data();
1152
1153}
1154
1155static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1156{
1157 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1158
1159 if ( !nl.count() )
1160 {
1161 QgsDebugMsg( QStringLiteral( "unable to find qgis element" ) );
1162 return;
1163 }
1164
1165 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1166
1167 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1168 lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
1169 lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
1170 lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral( "saveDateTime" ), QString() ), Qt::ISODate );
1171}
1172
1173
1174QgsProjectVersion getVersion( const QDomDocument &doc )
1175{
1176 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1177
1178 if ( !nl.count() )
1179 {
1180 QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
1181 return QgsProjectVersion( 0, 0, 0, QString() );
1182 }
1183
1184 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1185
1186 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1187 QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
1188 return projectVersion;
1189}
1190
1191
1193{
1194 return mSnappingConfig;
1195}
1196
1198{
1199 if ( mSnappingConfig == snappingConfig )
1200 return;
1201
1202 mSnappingConfig = snappingConfig;
1203 setDirty( true );
1204 emit snappingConfigChanged( mSnappingConfig );
1205}
1206
1208{
1209 if ( mAvoidIntersectionsMode == mode )
1210 return;
1211
1212 mAvoidIntersectionsMode = mode;
1214}
1215
1216bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, Qgis::ProjectReadFlags flags )
1217{
1218 // Layer order is set by the restoring the legend settings from project file.
1219 // This is done on the 'readProject( ... )' signal
1220
1221 QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
1222
1223 // process the map layer nodes
1224
1225 if ( layerElement.isNull() ) // if we have no layers to process, bail
1226 {
1227 return true; // Decided to return "true" since it's
1228 // possible for there to be a project with no
1229 // layers; but also, more imporantly, this
1230 // would cause the tests/qgsproject to fail
1231 // since the test suite doesn't currently
1232 // support test layers
1233 }
1234
1235 bool returnStatus = true;
1236 int numLayers = 0;
1237
1238 while ( ! layerElement.isNull() )
1239 {
1240 numLayers++;
1241 layerElement = layerElement.nextSiblingElement( QStringLiteral( "maplayer" ) );
1242 }
1243
1244 // order layers based on their dependencies
1245 QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), QStringLiteral( "projectload" ) );
1246 const QgsLayerDefinition::DependencySorter depSorter( doc );
1247 if ( depSorter.hasCycle() )
1248 return false;
1249
1250 // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1251 if ( depSorter.hasMissingDependency() )
1252 returnStatus = false;
1253
1254 emit layerLoaded( 0, numLayers );
1255
1256 const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1257 const int totalLayerCount = sortedLayerNodes.count();
1258
1259 int i = 0;
1260 for ( const QDomNode &node : sortedLayerNodes )
1261 {
1262 const QDomElement element = node.toElement();
1263
1264 const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1265 if ( !name.isNull() )
1266 emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1267
1268 profile.switchTask( name );
1269
1270 if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1271 {
1272 createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1273 }
1274 else
1275 {
1276 QgsReadWriteContext context;
1277 context.setPathResolver( pathResolver() );
1278 context.setProjectTranslator( this );
1280
1281 if ( !addLayer( element, brokenNodes, context, flags ) )
1282 {
1283 returnStatus = false;
1284 }
1285 const auto messages = context.takeMessages();
1286 if ( !messages.isEmpty() )
1287 {
1288 emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1289 }
1290 }
1291 emit layerLoaded( i + 1, totalLayerCount );
1292 i++;
1293 }
1294
1295 return returnStatus;
1296}
1297
1298bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, Qgis::ProjectReadFlags flags )
1299{
1300 const QString type = layerElem.attribute( QStringLiteral( "type" ) );
1301 QgsDebugMsgLevel( "Layer type is " + type, 4 );
1302 std::unique_ptr<QgsMapLayer> mapLayer;
1303
1304 QgsScopedRuntimeProfile profile( tr( "Create layer" ), QStringLiteral( "projectload" ) );
1305
1306 bool ok = false;
1307 const QgsMapLayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1308 if ( !ok )
1309 {
1310 QgsDebugMsg( QStringLiteral( "Unknown layer type \"%1\"" ).arg( type ) );
1311 return false;
1312 }
1313
1314 switch ( layerType )
1315 {
1317 mapLayer = std::make_unique<QgsVectorLayer>();
1318 break;
1319
1321 mapLayer = std::make_unique<QgsRasterLayer>();
1322 break;
1323
1325 mapLayer = std::make_unique<QgsMeshLayer>();
1326 break;
1327
1329 mapLayer = std::make_unique<QgsVectorTileLayer>();
1330 break;
1331
1333 mapLayer = std::make_unique<QgsPointCloudLayer>();
1334 break;
1335
1337 {
1338 const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1339 mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1340 break;
1341 }
1342
1344 {
1345 const QgsAnnotationLayer::LayerOptions options( mTransformContext );
1346 mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1347 break;
1348 }
1349
1351 {
1352 const QgsGroupLayer::LayerOptions options( mTransformContext );
1353 mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1354 break;
1355 }
1356 }
1357
1358 if ( !mapLayer )
1359 {
1360 QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1361 return false;
1362 }
1363
1364 Q_CHECK_PTR( mapLayer ); // NOLINT
1365
1366 // This is tricky: to avoid a leak we need to check if the layer was already in the store
1367 // because if it was, the newly created layer will not be added to the store and it would leak.
1368 const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1369 Q_ASSERT( ! layerId.isEmpty() );
1370 const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1371
1372 // have the layer restore state that is stored in Dom node
1373 QgsMapLayer::ReadFlags layerFlags = QgsMapLayer::ReadFlags();
1376 // Propagate trust layer metadata flag
1379 // Propagate open layers in read-only mode
1381 layerFlags |= QgsMapLayer::FlagForceReadOnly;
1382
1383 profile.switchTask( tr( "Load layer source" ) );
1384 const bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1385
1386 // apply specific settings to vector layer
1387 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1388 {
1390 if ( vl->dataProvider() )
1391 {
1393 vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, evaluateDefaultValues );
1394 }
1395 }
1396
1397 profile.switchTask( tr( "Add layer to project" ) );
1398 QList<QgsMapLayer *> newLayers;
1399 newLayers << mapLayer.get();
1400 if ( layerIsValid || flags & Qgis::ProjectReadFlag::DontResolveLayers )
1401 {
1402 emit readMapLayer( mapLayer.get(), layerElem );
1403 addMapLayers( newLayers );
1404 // Try to resolve references here (this is necessary to set up joined fields that will be possibly used by
1405 // virtual layers that point to this layer's joined field in their query otherwise they won't be valid ),
1406 // a second attempt to resolve references will be done after all layers are loaded
1407 // see https://github.com/qgis/QGIS/issues/46834
1408 if ( QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1409 {
1410 vLayer->joinBuffer()->resolveReferences( this );
1411 }
1412 }
1413 else
1414 {
1415 // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1416 addMapLayers( newLayers, false );
1417 newLayers.first();
1418 QgsDebugMsg( "Unable to load " + type + " layer" );
1419 brokenNodes.push_back( layerElem );
1420 }
1421
1422 const bool wasEditable = layerElem.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
1423 if ( wasEditable )
1424 {
1425 mapLayer->setCustomProperty( QStringLiteral( "_layer_was_editable" ), true );
1426 }
1427 else
1428 {
1429 mapLayer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
1430 }
1431
1432 // It should be safe to delete the layer now if layer was stored, because all the store
1433 // had to to was to reset the data source in case the validity changed.
1434 if ( ! layerWasStored )
1435 {
1436 mapLayer.release();
1437 }
1438
1439 return layerIsValid;
1440}
1441
1442bool QgsProject::read( const QString &filename, Qgis::ProjectReadFlags flags )
1443{
1444 mFile.setFileName( filename );
1445 mCachedHomePath.clear();
1446 mProjectScope.reset();
1447
1448 return read( flags );
1449}
1450
1451bool QgsProject::read( Qgis::ProjectReadFlags flags )
1452{
1453 const QString filename = mFile.fileName();
1454 bool returnValue;
1455
1456 if ( QgsProjectStorage *storage = projectStorage() )
1457 {
1458 QTemporaryFile inDevice;
1459 if ( !inDevice.open() )
1460 {
1461 setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1462 return false;
1463 }
1464
1465 QgsReadWriteContext context;
1466 context.setProjectTranslator( this );
1467 if ( !storage->readProject( filename, &inDevice, context ) )
1468 {
1469 QString err = tr( "Unable to open %1" ).arg( filename );
1470 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1471 if ( !messages.isEmpty() )
1472 err += QStringLiteral( "\n\n" ) + messages.last().message();
1473 setError( err );
1474 return false;
1475 }
1476 returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1477 }
1478 else
1479 {
1480 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1481 {
1482 returnValue = unzip( mFile.fileName(), flags );
1483 }
1484 else
1485 {
1486 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1487 const QFileInfo finfo( mFile.fileName() );
1488 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1489 if ( QFile( attachmentsZip ).exists() )
1490 {
1491 std::unique_ptr<QgsArchive> archive( new QgsArchive() );
1492 if ( archive->unzip( attachmentsZip ) )
1493 {
1494 mArchive = std::move( archive );
1495 }
1496 }
1497 returnValue = readProjectFile( mFile.fileName(), flags );
1498 }
1499
1500 //on translation we should not change the filename back
1501 if ( !mTranslator )
1502 {
1503 mFile.setFileName( filename );
1504 mCachedHomePath.clear();
1505 mProjectScope.reset();
1506 }
1507 else
1508 {
1509 //but delete the translator
1510 mTranslator.reset( nullptr );
1511 }
1512 }
1513 emit homePathChanged();
1514 return returnValue;
1515}
1516
1517bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlags flags )
1518{
1519 // avoid multiple emission of snapping updated signals
1520 ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
1521
1522 QFile projectFile( filename );
1523 clearError();
1524
1525 QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
1526 QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), QStringLiteral( "projectload" ) );
1527
1528 const QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), QgsApplication::settingsLocaleUserLocale.value() );
1529
1530 if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1531 {
1532 mTranslator.reset( new QTranslator() );
1533 ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1534 }
1535
1536 profile.switchTask( tr( "Reading project file" ) );
1537 std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1538
1539 if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1540 {
1541 projectFile.close();
1542
1543 setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1544
1545 return false;
1546 }
1547
1548 // location of problem associated with errorMsg
1549 int line, column;
1550 QString errorMsg;
1551
1552 if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1553 {
1554 const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1555 .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1556
1557 QgsDebugMsg( errorString );
1558
1559 projectFile.close();
1560
1561 setError( errorString );
1562
1563 return false;
1564 }
1565
1566 projectFile.close();
1567
1568 QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1569
1570 // get project version string, if any
1571 const QgsProjectVersion fileVersion = getVersion( *doc );
1572 const QgsProjectVersion thisVersion( Qgis::version() );
1573
1574 profile.switchTask( tr( "Updating project file" ) );
1575 if ( thisVersion > fileVersion )
1576 {
1577 const bool isOlderMajorVersion = fileVersion.majorVersion() < thisVersion.majorVersion();
1578
1579 if ( isOlderMajorVersion )
1580 {
1581 QgsLogger::warning( "Loading a file that was saved with an older "
1582 "version of qgis (saved in " + fileVersion.text() +
1583 ", loaded in " + Qgis::version() +
1584 "). Problems may occur." );
1585 }
1586
1587 QgsProjectFileTransform projectFile( *doc, fileVersion );
1588
1589 // Shows a warning when an old project file is read.
1591 emit oldProjectVersionWarning( fileVersion.text() );
1593 emit readVersionMismatchOccurred( fileVersion.text() );
1594
1595 projectFile.updateRevision( thisVersion );
1596 }
1597 else if ( fileVersion > thisVersion )
1598 {
1599 QgsLogger::warning( "Loading a file that was saved with a newer "
1600 "version of qgis (saved in " + fileVersion.text() +
1601 ", loaded in " + Qgis::version() +
1602 "). Problems may occur." );
1603
1604 emit readVersionMismatchOccurred( fileVersion.text() );
1605 }
1606
1607 // start new project, just keep the file name and auxiliary storage
1608 profile.switchTask( tr( "Creating auxiliary storage" ) );
1609 const QString fileName = mFile.fileName();
1610 std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1611 std::unique_ptr<QgsArchive> archive = std::move( mArchive );
1612 clear();
1613 mAuxiliaryStorage = std::move( aStorage );
1614 mArchive = std::move( archive );
1615 mFile.setFileName( fileName );
1616 mCachedHomePath.clear();
1617 mProjectScope.reset();
1618 mSaveVersion = fileVersion;
1619
1620 // now get any properties
1621 profile.switchTask( tr( "Reading properties" ) );
1622 _getProperties( *doc, mProperties );
1623
1624 // now get the data defined server properties
1625 mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
1626
1627 QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1628
1629#if 0
1630 dump_( mProperties );
1631#endif
1632
1633 // get older style project title
1634 QString oldTitle;
1635 _getTitle( *doc, oldTitle );
1636
1637 readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
1638
1639 const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1640 if ( homePathNl.count() > 0 )
1641 {
1642 const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1643 const QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1644 if ( !homePath.isEmpty() )
1646 }
1647 else
1648 {
1649 emit homePathChanged();
1650 }
1651
1652 const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1653 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1654 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1656 const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1657 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1658 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1659 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1661
1662
1663 const QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
1664 if ( !distanceUnitString.isEmpty() )
1665 setDistanceUnits( QgsUnitTypes::decodeDistanceUnit( distanceUnitString ) );
1666
1667 const QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
1668 if ( !areaUnitString.isEmpty() )
1669 setAreaUnits( QgsUnitTypes::decodeAreaUnit( areaUnitString ) );
1670
1671 QgsReadWriteContext context;
1672 context.setPathResolver( pathResolver() );
1673 context.setProjectTranslator( this );
1674
1675 //crs
1677 if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1678 {
1679 // first preference - dedicated projectCrs node
1680 const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1681 if ( !srsNode.isNull() )
1682 {
1683 projectCrs.readXml( srsNode );
1684 }
1685
1686 if ( !projectCrs.isValid() )
1687 {
1688 const QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1689 const long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1690 const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1691
1692 // authid should be prioritized over all
1693 const bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
1694 if ( !authid.isEmpty() && !isUserAuthId )
1695 projectCrs = QgsCoordinateReferenceSystem( authid );
1696
1697 // try the CRS
1698 if ( !projectCrs.isValid() && currentCRS >= 0 )
1699 {
1700 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1701 }
1702
1703 // if that didn't produce a match, try the proj.4 string
1704 if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
1705 {
1706 projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
1707 }
1708
1709 // last just take the given id
1710 if ( !projectCrs.isValid() )
1711 {
1712 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1713 }
1714 }
1715 }
1716 mCrs = projectCrs;
1717
1718 QStringList datumErrors;
1719 if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1720 {
1721 emit missingDatumTransforms( datumErrors );
1722 }
1724
1725 //add variables defined in project file - do this early in the reading cycle, as other components
1726 //(e.g. layouts) may depend on these variables
1727 const QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1728 const QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1729
1730 mCustomVariables.clear();
1731 if ( variableNames.length() == variableValues.length() )
1732 {
1733 for ( int i = 0; i < variableNames.length(); ++i )
1734 {
1735 mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1736 }
1737 }
1738 else
1739 {
1740 QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1741 }
1742
1743 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectMetadata" ) );
1744
1745 if ( !element.isNull() )
1746 {
1747 mMetadata.readMetadataXml( element );
1748 }
1749 else
1750 {
1751 // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1752 mMetadata = QgsProjectMetadata();
1753 }
1754 if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1755 {
1756 // upgrade older title storage to storing within project metadata.
1757 mMetadata.setTitle( oldTitle );
1758 }
1759 emit metadataChanged();
1760
1761 // Transaction mode
1762 element = doc->documentElement().firstChildElement( QStringLiteral( "transaction" ) );
1763 if ( !element.isNull() )
1764 {
1765 mTransactionMode = qgsEnumKeyToValue( element.attribute( QStringLiteral( "mode" ) ), Qgis::TransactionMode::Disabled );
1766 }
1767 else
1768 {
1769 // maybe older project => try read autotransaction
1770 element = doc->documentElement().firstChildElement( QStringLiteral( "autotransaction" ) );
1771 if ( ! element.isNull() )
1772 {
1773 mTransactionMode = static_cast<Qgis::TransactionMode>( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() );
1774 }
1775 }
1776
1777 // read the layer tree from project file
1778 profile.switchTask( tr( "Loading layer tree" ) );
1779 mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1780
1781 QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1782 if ( !layerTreeElem.isNull() )
1783 {
1784 // Use a temporary tree to read the nodes to prevent signals being delivered to the models
1785 QgsLayerTree tempTree;
1786 tempTree.readChildrenFromXml( layerTreeElem, context );
1787 mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
1788 }
1789 else
1790 {
1791 QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1792 }
1793
1794 mLayerTreeRegistryBridge->setEnabled( false );
1795
1796 // get the map layers
1797 profile.switchTask( tr( "Reading map layers" ) );
1798
1799 loadProjectFlags( doc.get() );
1800
1801 QList<QDomNode> brokenNodes;
1802 const bool clean = _getMapLayers( *doc, brokenNodes, flags );
1803
1804 // review the integrity of the retrieved map layers
1805 if ( !clean && !( flags & Qgis::ProjectReadFlag::DontResolveLayers ) )
1806 {
1807 QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1808
1809 if ( !brokenNodes.isEmpty() )
1810 {
1811 QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1812 }
1813
1814 // we let a custom handler decide what to do with missing layers
1815 // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1816 mBadLayerHandler->handleBadLayers( brokenNodes );
1817 }
1818
1819 mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
1820 mMainAnnotationLayer->setTransformContext( mTransformContext );
1821
1822 // load embedded groups and layers
1823 profile.switchTask( tr( "Loading embedded layers" ) );
1824 loadEmbeddedNodes( mRootGroup, flags );
1825
1826 // Resolve references to other layers
1827 // Needs to be done here once all dependent layers are loaded
1828 profile.switchTask( tr( "Resolving layer references" ) );
1829 QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1830 for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
1831 {
1832 it.value()->resolveReferences( this );
1833 }
1834
1835 mLayerTreeRegistryBridge->setEnabled( true );
1836
1837 // now that layers are loaded, we can resolve layer tree's references to the layers
1838 profile.switchTask( tr( "Resolving references" ) );
1839 mRootGroup->resolveReferences( this );
1840
1841 if ( !layerTreeElem.isNull() )
1842 {
1843 mRootGroup->readLayerOrderFromXml( layerTreeElem );
1844 }
1845
1846 // Load pre 3.0 configuration
1847 const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1848 if ( !layerTreeCanvasElem.isNull( ) )
1849 {
1850 mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1851 }
1852
1853 // Convert pre 3.4 to create layers flags
1854 if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
1855 {
1856 const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1857 for ( const QString &layerId : requiredLayerIds )
1858 {
1859 if ( QgsMapLayer *layer = mapLayer( layerId ) )
1860 {
1861 layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1862 }
1863 }
1864 const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1865 for ( const QString &layerId : disabledLayerIds )
1866 {
1867 if ( QgsMapLayer *layer = mapLayer( layerId ) )
1868 {
1869 layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1870 }
1871 }
1872 }
1873
1874 // Convert pre 3.26 default styles
1875 if ( QgsProjectVersion( 3, 26, 0 ) > mSaveVersion )
1876 {
1877 // Convert default symbols
1878 QString styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
1879 if ( !styleName.isEmpty() )
1880 {
1881 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1883 }
1884 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
1885 if ( !styleName.isEmpty() )
1886 {
1887 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1889 }
1890 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
1891 if ( !styleName.isEmpty() )
1892 {
1893 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1895 }
1896 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
1897 if ( !styleName.isEmpty() )
1898 {
1899 std::unique_ptr<QgsColorRamp> colorRamp( QgsStyle::defaultStyle()->colorRamp( styleName ) );
1900 styleSettings()->setDefaultColorRamp( colorRamp.get() );
1901 }
1902
1903 // Convert randomize default symbol fill color
1904 styleSettings()->setRandomizeDefaultSymbolColor( readBoolEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ), true ) );
1905
1906 // Convert default symbol opacity
1907 double opacity = 1.0;
1908 bool ok = false;
1909 // upgrade old setting
1910 double alpha = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ), 255, &ok );
1911 if ( ok )
1912 opacity = alpha / 255.0;
1913 double newOpacity = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ), 1.0, &ok );
1914 if ( ok )
1915 opacity = newOpacity;
1917
1918 // Cleanup
1919 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
1920 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
1921 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
1922 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
1923 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ) );
1924 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ) );
1925 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ) );
1926 }
1927
1928 // After bad layer handling we might still have invalid layers,
1929 // store them in case the user wanted to handle them later
1930 // or wanted to pass them through when saving
1932 {
1933 profile.switchTask( tr( "Storing original layer properties" ) );
1934 QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1935 }
1936
1937 mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1938
1939 profile.switchTask( tr( "Loading map themes" ) );
1940 mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1942 mMapThemeCollection->readXml( *doc );
1943
1944 profile.switchTask( tr( "Loading label settings" ) );
1945 mLabelingEngineSettings->readSettingsFromProject( this );
1947
1948 profile.switchTask( tr( "Loading annotations" ) );
1949 mAnnotationManager->readXml( doc->documentElement(), context );
1951 {
1952 profile.switchTask( tr( "Loading layouts" ) );
1953 mLayoutManager->readXml( doc->documentElement(), *doc );
1954 }
1955
1957 {
1958 profile.switchTask( tr( "Loading 3D Views" ) );
1959 m3DViewsManager->readXml( doc->documentElement(), *doc );
1960 }
1961
1962 profile.switchTask( tr( "Loading bookmarks" ) );
1963 mBookmarkManager->readXml( doc->documentElement(), *doc );
1964
1965 // reassign change dependencies now that all layers are loaded
1966 QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1967 for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
1968 {
1969 it.value()->setDependencies( it.value()->dependencies() );
1970 }
1971
1972 profile.switchTask( tr( "Loading snapping settings" ) );
1973 mSnappingConfig.readProject( *doc );
1974 mAvoidIntersectionsMode = static_cast<Qgis::AvoidIntersectionsMode>( readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( Qgis::AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
1975
1976 profile.switchTask( tr( "Loading view settings" ) );
1977 // restore older project scales settings
1978 mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
1979 const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
1980 QVector<double> res;
1981 for ( const QString &scale : scales )
1982 {
1983 const QStringList parts = scale.split( ':' );
1984 if ( parts.size() != 2 )
1985 continue;
1986
1987 bool ok = false;
1988 const double denominator = QLocale().toDouble( parts[1], &ok );
1989 if ( ok )
1990 {
1991 res << denominator;
1992 }
1993 }
1994 mViewSettings->setMapScales( res );
1995 const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
1996 if ( !viewSettingsElement.isNull() )
1997 mViewSettings->readXml( viewSettingsElement, context );
1998
1999 // restore style settings
2000 profile.switchTask( tr( "Loading style properties" ) );
2001 const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectStyleSettings" ) );
2002 if ( !styleSettingsElement.isNull() )
2003 mStyleSettings->readXml( styleSettingsElement, context, flags );
2004
2005 // restore time settings
2006 profile.switchTask( tr( "Loading temporal settings" ) );
2007 const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectTimeSettings" ) );
2008 if ( !timeSettingsElement.isNull() )
2009 mTimeSettings->readXml( timeSettingsElement, context );
2010
2011
2012 profile.switchTask( tr( "Loading elevation properties" ) );
2013 const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( QStringLiteral( "ElevationProperties" ) );
2014 if ( !elevationPropertiesElement.isNull() )
2015 mElevationProperties->readXml( elevationPropertiesElement, context );
2016 mElevationProperties->resolveReferences( this );
2017
2018 profile.switchTask( tr( "Loading display settings" ) );
2019 const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
2020 if ( !displaySettingsElement.isNull() )
2021 mDisplaySettings->readXml( displaySettingsElement, context );
2022
2023 profile.switchTask( tr( "Updating variables" ) );
2025 profile.switchTask( tr( "Updating CRS" ) );
2026 emit crsChanged();
2027 emit ellipsoidChanged( ellipsoid() );
2028
2029 // read the project: used by map canvas and legend
2030 profile.switchTask( tr( "Reading external settings" ) );
2031 emit readProject( *doc );
2032 emit readProjectWithContext( *doc, context );
2033
2034 profile.switchTask( tr( "Updating interface" ) );
2035
2036 snapSignalBlock.release();
2037 if ( !mBlockSnappingUpdates )
2038 emit snappingConfigChanged( mSnappingConfig );
2039
2042 emit projectColorsChanged();
2043
2044 // if all went well, we're allegedly in pristine state
2045 if ( clean )
2046 setDirty( false );
2047
2048 QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUser ), 2 );
2049 QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUserFull ), 2 );
2050
2054
2055 if ( mTranslator )
2056 {
2057 //project possibly translated -> rename it with locale postfix
2058 const QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
2059 setFileName( newFileName );
2060
2061 if ( write() )
2062 {
2063 setTitle( localeFileName );
2064 QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Success );
2065 }
2066 else
2067 {
2068 QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Critical );
2069 }
2070 }
2071
2072 // lastly, make any previously editable layers editable
2073 const QMap<QString, QgsMapLayer *> loadedLayers = mapLayers();
2074 for ( auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2075 {
2076 if ( it.value()->isValid() && it.value()->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2077 {
2078 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2079 vl->startEditing();
2080 it.value()->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2081 }
2082 }
2083
2084 return true;
2085}
2086
2087
2088bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
2089{
2090 bool valid = true;
2091 const auto constChildren = group->children();
2092 for ( QgsLayerTreeNode *child : constChildren )
2093 {
2094 if ( QgsLayerTree::isGroup( child ) )
2095 {
2096 QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
2097 if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2098 {
2099 // make sure to convert the path from relative to absolute
2100 const QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
2101 childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
2102 QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
2103 if ( newGroup )
2104 {
2105 QList<QgsLayerTreeNode *> clonedChildren;
2106 const QList<QgsLayerTreeNode *> constChildren = newGroup->children();
2107 clonedChildren.reserve( constChildren.size() );
2108 for ( QgsLayerTreeNode *newGroupChild : constChildren )
2109 clonedChildren << newGroupChild->clone();
2110 delete newGroup;
2111
2112 childGroup->insertChildNodes( 0, clonedChildren );
2113 }
2114 }
2115 else
2116 {
2117 loadEmbeddedNodes( childGroup, flags );
2118 }
2119 }
2120 else if ( QgsLayerTree::isLayer( child ) )
2121 {
2122 if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2123 {
2124 QList<QDomNode> brokenNodes;
2125 if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
2126 {
2127 valid = valid && false;
2128 }
2129 }
2130 }
2131
2132 }
2133
2134 return valid;
2135}
2136
2138{
2139 return mCustomVariables;
2140}
2141
2142void QgsProject::setCustomVariables( const QVariantMap &variables )
2143{
2144 if ( variables == mCustomVariables )
2145 return;
2146
2147 //write variable to project
2148 QStringList variableNames;
2149 QStringList variableValues;
2150
2151 QVariantMap::const_iterator it = variables.constBegin();
2152 for ( ; it != variables.constEnd(); ++it )
2153 {
2154 variableNames << it.key();
2155 variableValues << it.value().toString();
2156 }
2157
2158 writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
2159 writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
2160
2161 mCustomVariables = variables;
2162 mProjectScope.reset();
2163
2165}
2166
2168{
2169 *mLabelingEngineSettings = settings;
2171}
2172
2174{
2175 return *mLabelingEngineSettings;
2176}
2177
2179{
2180 mProjectScope.reset();
2181 return mLayerStore.get();
2182}
2183
2185{
2186 return mLayerStore.get();
2187}
2188
2189QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
2190{
2191 QList<QgsVectorLayer *> layers;
2192 const QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
2193 const auto constLayerIds = layerIds;
2194 for ( const QString &layerId : constLayerIds )
2195 {
2196 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
2197 layers << vlayer;
2198 }
2199 return layers;
2200}
2201
2202void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
2203{
2204 QStringList list;
2205 list.reserve( layers.size() );
2206 for ( QgsVectorLayer *layer : layers )
2207 list << layer->id();
2208 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
2210}
2211
2213{
2214 QgsExpressionContext context;
2215
2218
2219 return context;
2220}
2221
2223{
2224 // MUCH cheaper to clone than build
2225 if ( mProjectScope )
2226 {
2227 std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2228 // we can't cache these
2229 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
2230 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
2231 return projectScope.release();
2232 }
2233
2234 mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
2235
2236 const QVariantMap vars = customVariables();
2237
2238 QVariantMap::const_iterator it = vars.constBegin();
2239
2240 for ( ; it != vars.constEnd(); ++it )
2241 {
2242 mProjectScope->setVariable( it.key(), it.value(), true );
2243 }
2244
2245 QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
2246 if ( projectPath.isEmpty() )
2247 projectPath = mOriginalPath;
2248 const QString projectFolder = QFileInfo( projectPath ).path();
2249 const QString projectFilename = QFileInfo( projectPath ).fileName();
2250 const QString projectBasename = baseName();
2251
2252 //add other known project variables
2253 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
2254 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
2255 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
2256 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
2257 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
2258 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
2259 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_last_saved" ), mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
2260 const QgsCoordinateReferenceSystem projectCrs = crs();
2261 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
2262 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
2263 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_description" ), projectCrs.description(), true, true ) );
2264 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
2265 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
2266 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_units" ), QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
2267 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_acronym" ), projectCrs.projectionAcronym(), true ) );
2268 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_ellipsoid" ), projectCrs.ellipsoidAcronym(), true ) );
2269 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_proj4" ), projectCrs.toProj(), true ) );
2270 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_wkt" ), projectCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), true ) );
2271
2272 // metadata
2273 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
2274 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
2275 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
2276 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
2277
2278 // keywords
2279 QVariantMap keywords;
2280 const QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2281 for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2282 {
2283 keywords.insert( it.key(), it.value() );
2284 }
2285 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
2286
2287 // layers
2288 QVariantList layersIds;
2289 QVariantList layers;
2290 const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2291 layersIds.reserve( layersInProject.count() );
2292 layers.reserve( layersInProject.count() );
2293 for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2294 {
2295 layersIds << it.value()->id();
2296 layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( it.value() ) );
2297 }
2298 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_ids" ), layersIds, true ) );
2299 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
2300
2301 mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
2302
2304}
2305
2306void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
2307{
2308 const QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2309
2310 const auto constLayers = layers;
2311 for ( QgsMapLayer *layer : constLayers )
2312 {
2313 if ( ! layer->isValid() )
2314 return;
2315
2316 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
2317 {
2318 vlayer->setReadExtentFromXml( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics );
2319 if ( vlayer->dataProvider() )
2320 vlayer->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues,
2322 }
2323
2324 connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
2325
2326 // check if we have to update connections for layers with dependencies
2327 for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2328 {
2329 const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2330 if ( deps.contains( layer->id() ) )
2331 {
2332 // reconnect to change signals
2333 it.value()->setDependencies( deps );
2334 }
2335 }
2336 }
2337
2338 updateTransactionGroups();
2339
2340 if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
2341 emit snappingConfigChanged( mSnappingConfig );
2342}
2343
2344void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
2345{
2346 if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
2347 emit snappingConfigChanged( mSnappingConfig );
2348}
2349
2350void QgsProject::cleanTransactionGroups( bool force )
2351{
2352 bool changed = false;
2353 for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2354 {
2355 if ( tg.value()->isEmpty() || force )
2356 {
2357 delete tg.value();
2358 tg = mTransactionGroups.erase( tg );
2359 changed = true;
2360 }
2361 else
2362 {
2363 ++tg;
2364 }
2365 }
2366 if ( changed )
2368}
2369
2370void QgsProject::updateTransactionGroups()
2371{
2372 mEditBufferGroup.clear();
2373
2374 switch ( mTransactionMode )
2375 {
2377 {
2378 cleanTransactionGroups( true );
2379 return;
2380 }
2381 break;
2383 cleanTransactionGroups( true );
2384 break;
2386 cleanTransactionGroups( false );
2387 break;
2388 }
2389
2390 bool tgChanged = false;
2391 const auto constLayers = mapLayers().values();
2392 for ( QgsMapLayer *layer : constLayers )
2393 {
2394 if ( ! layer->isValid() )
2395 continue;
2396
2397 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2398 if ( ! vlayer )
2399 continue;
2400
2401 switch ( mTransactionMode )
2402 {
2404 Q_ASSERT( false );
2405 break;
2407 {
2409 {
2410 const QString connString = QgsTransaction::connectionString( vlayer->source() );
2411 const QString key = vlayer->providerType();
2412
2413 QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
2414
2415 if ( !tg )
2416 {
2417 tg = new QgsTransactionGroup();
2418 mTransactionGroups.insert( qMakePair( key, connString ), tg );
2419 tgChanged = true;
2420 }
2421 tg->addLayer( vlayer );
2422 }
2423 }
2424 break;
2426 {
2427 if ( vlayer->supportsEditing() )
2428 mEditBufferGroup.addLayer( vlayer );
2429 }
2430 break;
2431 }
2432 }
2433
2434 if ( tgChanged )
2436}
2437
2438bool QgsProject::readLayer( const QDomNode &layerNode )
2439{
2440 QgsReadWriteContext context;
2441 context.setPathResolver( pathResolver() );
2442 context.setProjectTranslator( this );
2444 QList<QDomNode> brokenNodes;
2445 if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
2446 {
2447 // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
2448 // added layer for joins
2449 const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
2450 for ( QgsVectorLayer *layer : vectorLayers )
2451 {
2452 // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
2453 layer->resolveReferences( this );
2454
2455 if ( layer->isValid() && layer->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2456 {
2457 layer->startEditing();
2458 layer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2459 }
2460 }
2461 return true;
2462 }
2463 return false;
2464}
2465
2466bool QgsProject::write( const QString &filename )
2467{
2468 mFile.setFileName( filename );
2469 mCachedHomePath.clear();
2470 return write();
2471}
2472
2474{
2475 mProjectScope.reset();
2476 if ( QgsProjectStorage *storage = projectStorage() )
2477 {
2478 QgsReadWriteContext context;
2479 // for projects stored in a custom storage, we have to check for the support
2480 // of relative paths since the storage most likely will not be in a file system
2481 const QString storageFilePath { storage->filePath( mFile.fileName() ) };
2482 if ( storageFilePath.isEmpty() )
2483 {
2485 }
2486 context.setPathResolver( pathResolver() );
2487
2488 const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
2489 const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
2490
2491 if ( !zip( tmpZipFilename ) )
2492 return false; // zip() already calls setError() when returning false
2493
2494 QFile tmpZipFile( tmpZipFilename );
2495 if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
2496 {
2497 setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
2498 return false;
2499 }
2500
2502 if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
2503 {
2504 QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
2505 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2506 if ( !messages.isEmpty() )
2507 err += QStringLiteral( "\n\n" ) + messages.last().message();
2508 setError( err );
2509 return false;
2510 }
2511
2512 tmpZipFile.close();
2513 QFile::remove( tmpZipFilename );
2514
2515 return true;
2516 }
2517
2518 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2519 {
2520 return zip( mFile.fileName() );
2521 }
2522 else
2523 {
2524 // write project file even if the auxiliary storage is not correctly
2525 // saved
2526 const bool asOk = saveAuxiliaryStorage();
2527 const bool writeOk = writeProjectFile( mFile.fileName() );
2528 bool attachmentsOk = true;
2529 if ( !mArchive->files().isEmpty() )
2530 {
2531 const QFileInfo finfo( mFile.fileName() );
2532 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
2533 attachmentsOk = mArchive->zip( attachmentsZip );
2534 }
2535
2536 // errors raised during writing project file are more important
2537 if ( ( !asOk || !attachmentsOk ) && writeOk )
2538 {
2539 QStringList errorMessage;
2540 if ( !asOk )
2541 {
2542 const QString err = mAuxiliaryStorage->errorString();
2543 errorMessage.append( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2544 }
2545 if ( !attachmentsOk )
2546 {
2547 errorMessage.append( tr( "Unable to save attachments archive" ) );
2548 }
2549 setError( errorMessage.join( '\n' ) );
2550 }
2551
2552 return asOk && writeOk && attachmentsOk;
2553 }
2554}
2555
2556bool QgsProject::writeProjectFile( const QString &filename )
2557{
2558 QFile projectFile( filename );
2559 clearError();
2560
2561 // if we have problems creating or otherwise writing to the project file,
2562 // let's find out up front before we go through all the hand-waving
2563 // necessary to create all the Dom objects
2564 const QFileInfo myFileInfo( projectFile );
2565 if ( myFileInfo.exists() && !myFileInfo.isWritable() )
2566 {
2567 setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
2568 .arg( projectFile.fileName() ) );
2569 return false;
2570 }
2571
2572 QgsReadWriteContext context;
2573 context.setPathResolver( pathResolver() );
2575
2576 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
2577
2578 const QDomDocumentType documentType =
2579 QDomImplementation().createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
2580 QStringLiteral( "SYSTEM" ) );
2581 std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
2582
2583 QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
2584 qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
2585 qgisNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
2586
2587 if ( !mSettings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
2588 {
2589 const QString newSaveUser = QgsApplication::userLoginName();
2590 const QString newSaveUserFull = QgsApplication::userFullName();
2591 qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2592 qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2593 mSaveUser = newSaveUser;
2594 mSaveUserFull = newSaveUserFull;
2595 mSaveDateTime = QDateTime::currentDateTime();
2596 qgisNode.setAttribute( QStringLiteral( "saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
2597 }
2598 else
2599 {
2600 mSaveUser.clear();
2601 mSaveUserFull.clear();
2602 mSaveDateTime = QDateTime();
2603 }
2604 doc->appendChild( qgisNode );
2605 mSaveVersion = QgsProjectVersion( Qgis::version() );
2606
2607 QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2608 homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2609 qgisNode.appendChild( homePathNode );
2610
2611 // title
2612 QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2613 qgisNode.appendChild( titleNode );
2614
2615 QDomElement transactionNode = doc->createElement( QStringLiteral( "transaction" ) );
2616 transactionNode.setAttribute( QStringLiteral( "mode" ), qgsEnumValueToKey( mTransactionMode ) );
2617 qgisNode.appendChild( transactionNode );
2618
2619 QDomElement flagsNode = doc->createElement( QStringLiteral( "projectFlags" ) );
2620 flagsNode.setAttribute( QStringLiteral( "set" ), qgsFlagValueToKeys( mFlags ) );
2621 qgisNode.appendChild( flagsNode );
2622
2623 const QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
2624 titleNode.appendChild( titleText );
2625
2626 // write project CRS
2627 QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
2628 mCrs.writeXml( srsNode, *doc );
2629 qgisNode.appendChild( srsNode );
2630
2631 // write layer tree - make sure it is without embedded subgroups
2632 QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
2634 QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
2635
2636 clonedRoot->writeXml( qgisNode, context );
2637 delete clonedRoot;
2638
2639 mSnappingConfig.writeProject( *doc );
2640 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( mAvoidIntersectionsMode ) );
2641
2642 // let map canvas and legend write their information
2643 emit writeProject( *doc );
2644
2645 // within top level node save list of layers
2646 const QMap<QString, QgsMapLayer *> layers = mapLayers();
2647
2648 QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
2649 mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
2650 qgisNode.appendChild( annotationLayerNode );
2651
2652 // Iterate over layers in zOrder
2653 // Call writeXml() on each
2654 QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
2655
2656 QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
2657 while ( li != layers.end() )
2658 {
2659 QgsMapLayer *ml = li.value();
2660
2661 if ( ml )
2662 {
2663 const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
2664 if ( emIt == mEmbeddedLayers.constEnd() )
2665 {
2666 QDomElement maplayerElem;
2667 // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
2668 // not available, just write what we DO have
2669 if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
2670 {
2671 // general layer metadata
2672 maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2673 ml->writeLayerXml( maplayerElem, *doc, context );
2674
2676 maplayerElem.setAttribute( QStringLiteral( "editable" ), QStringLiteral( "1" ) );
2677 }
2678 else if ( ! ml->originalXmlProperties().isEmpty() )
2679 {
2680 QDomDocument document;
2681 if ( document.setContent( ml->originalXmlProperties() ) )
2682 {
2683 maplayerElem = document.firstChildElement();
2684 }
2685 else
2686 {
2687 QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
2688 }
2689 }
2690
2691 emit writeMapLayer( ml, maplayerElem, *doc );
2692
2693 projectLayersNode.appendChild( maplayerElem );
2694 }
2695 else
2696 {
2697 // layer defined in an external project file
2698 // only save embedded layer if not managed by a legend group
2699 if ( emIt.value().second )
2700 {
2701 QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2702 mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
2703 mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
2704 mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
2705 projectLayersNode.appendChild( mapLayerElem );
2706 }
2707 }
2708 }
2709 li++;
2710 }
2711
2712 qgisNode.appendChild( projectLayersNode );
2713
2714 QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
2715 const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
2716 for ( QgsMapLayer *layer : constCustomLayerOrder )
2717 {
2718 QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
2719 mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
2720 layerOrderNode.appendChild( mapLayerElem );
2721 }
2722 qgisNode.appendChild( layerOrderNode );
2723
2724 mLabelingEngineSettings->writeSettingsToProject( this );
2725
2726 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
2727 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
2728 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
2729
2730 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
2731 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
2732 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
2733 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
2734
2735 writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( mDistanceUnits ) );
2736 writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( mAreaUnits ) );
2737
2738 // now add the optional extra properties
2739#if 0
2740 dump_( mProperties );
2741#endif
2742
2743 QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 2 );
2744
2745 if ( !mProperties.isEmpty() ) // only worry about properties if we
2746 // actually have any properties
2747 {
2748 mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2749 }
2750
2751 QDomElement ddElem = doc->createElement( QStringLiteral( "dataDefinedServerProperties" ) );
2752 mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
2753 qgisNode.appendChild( ddElem );
2754
2755 mMapThemeCollection->writeXml( *doc );
2756
2757 mTransformContext.writeXml( qgisNode, context );
2758
2759 QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2760 mMetadata.writeMetadataXml( metadataElem, *doc );
2761 qgisNode.appendChild( metadataElem );
2762
2763 const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2764 qgisNode.appendChild( annotationsElem );
2765
2766 const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2767 qgisNode.appendChild( layoutElem );
2768
2769 const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
2770 qgisNode.appendChild( views3DElem );
2771
2772 const QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2773 qgisNode.appendChild( bookmarkElem );
2774
2775 const QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
2776 qgisNode.appendChild( viewSettingsElem );
2777
2778 const QDomElement styleSettingsElem = mStyleSettings->writeXml( *doc, context );
2779 qgisNode.appendChild( styleSettingsElem );
2780
2781 const QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
2782 qgisNode.appendChild( timeSettingsElement );
2783
2784 const QDomElement elevationPropertiesElement = mElevationProperties->writeXml( *doc, context );
2785 qgisNode.appendChild( elevationPropertiesElement );
2786
2787 const QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
2788 qgisNode.appendChild( displaySettingsElem );
2789
2790 // now wrap it up and ship it to the project file
2791 doc->normalize(); // XXX I'm not entirely sure what this does
2792
2793 // Create backup file
2794 if ( QFile::exists( fileName() ) )
2795 {
2796 QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2797 bool ok = true;
2798 ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2799 ok &= projectFile.open( QIODevice::ReadOnly );
2800
2801 QByteArray ba;
2802 while ( ok && !projectFile.atEnd() )
2803 {
2804 ba = projectFile.read( 10240 );
2805 ok &= backupFile.write( ba ) == ba.size();
2806 }
2807
2808 projectFile.close();
2809 backupFile.close();
2810
2811 if ( !ok )
2812 {
2813 setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2814 return false;
2815 }
2816
2817 const QFileInfo fi( fileName() );
2818 struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2819 utime( backupFile.fileName().toUtf8().constData(), &tb );
2820 }
2821
2822 if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2823 {
2824 projectFile.close(); // even though we got an error, let's make
2825 // sure it's closed anyway
2826
2827 setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2828 return false;
2829 }
2830
2831 QTemporaryFile tempFile;
2832 bool ok = tempFile.open();
2833 if ( ok )
2834 {
2835 QTextStream projectFileStream( &tempFile );
2836 doc->save( projectFileStream, 2 ); // save as utf-8
2837 ok &= projectFileStream.pos() > -1;
2838
2839 ok &= tempFile.seek( 0 );
2840
2841 QByteArray ba;
2842 while ( ok && !tempFile.atEnd() )
2843 {
2844 ba = tempFile.read( 10240 );
2845 ok &= projectFile.write( ba ) == ba.size();
2846 }
2847
2848 ok &= projectFile.error() == QFile::NoError;
2849
2850 projectFile.close();
2851 }
2852
2853 tempFile.close();
2854
2855 if ( !ok )
2856 {
2857 setError( tr( "Unable to save to file %1. Your project "
2858 "may be corrupted on disk. Try clearing some space on the volume and "
2859 "check file permissions before pressing save again." )
2860 .arg( projectFile.fileName() ) );
2861 return false;
2862 }
2863
2864 setDirty( false ); // reset to pristine state
2865
2866 emit projectSaved();
2867 return true;
2868}
2869
2870bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2871{
2872 bool propertiesModified;
2873 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2874
2875 if ( propertiesModified )
2876 setDirty( true );
2877
2878 return success;
2879}
2880
2881bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2882{
2883 bool propertiesModified;
2884 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2885
2886 if ( propertiesModified )
2887 setDirty( true );
2888
2889 return success;
2890}
2891
2892bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2893{
2894 bool propertiesModified;
2895 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2896
2897 if ( propertiesModified )
2898 setDirty( true );
2899
2900 return success;
2901}
2902
2903bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2904{
2905 bool propertiesModified;
2906 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2907
2908 if ( propertiesModified )
2909 setDirty( true );
2910
2911 return success;
2912}
2913
2914bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2915{
2916 bool propertiesModified;
2917 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2918
2919 if ( propertiesModified )
2920 setDirty( true );
2921
2922 return success;
2923}
2924
2925QStringList QgsProject::readListEntry( const QString &scope,
2926 const QString &key,
2927 const QStringList &def,
2928 bool *ok ) const
2929{
2930 QgsProjectProperty *property = findKey_( scope, key, mProperties );
2931
2932 QVariant value;
2933
2934 if ( property )
2935 {
2936 value = property->value();
2937
2938 const bool valid = QVariant::StringList == value.type();
2939 if ( ok )
2940 *ok = valid;
2941
2942 if ( valid )
2943 {
2944 return value.toStringList();
2945 }
2946 }
2947 else if ( ok )
2948 *ok = false;
2949
2950
2951 return def;
2952}
2953
2954
2955QString QgsProject::readEntry( const QString &scope,
2956 const QString &key,
2957 const QString &def,
2958 bool *ok ) const
2959{
2960 QgsProjectProperty *property = findKey_( scope, key, mProperties );
2961
2962 QVariant value;
2963
2964 if ( property )
2965 {
2966 value = property->value();
2967
2968 const bool valid = value.canConvert( QVariant::String );
2969 if ( ok )
2970 *ok = valid;
2971
2972 if ( valid )
2973 return value.toString();
2974 }
2975 else if ( ok )
2976 *ok = false;
2977
2978 return def;
2979}
2980
2981int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2982 bool *ok ) const
2983{
2984 QgsProjectProperty *property = findKey_( scope, key, mProperties );
2985
2986 QVariant value;
2987
2988 if ( property )
2989 {
2990 value = property->value();
2991 }
2992
2993 const bool valid = value.canConvert( QVariant::Int );
2994
2995 if ( ok )
2996 {
2997 *ok = valid;
2998 }
2999
3000 if ( valid )
3001 {
3002 return value.toInt();
3003 }
3004
3005 return def;
3006}
3007
3008double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
3009 double def,
3010 bool *ok ) const
3011{
3012 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3013 if ( property )
3014 {
3015 const QVariant value = property->value();
3016
3017 const bool valid = value.canConvert( QVariant::Double );
3018 if ( ok )
3019 *ok = valid;
3020
3021 if ( valid )
3022 return value.toDouble();
3023 }
3024 else if ( ok )
3025 *ok = false;
3026
3027 return def;
3028}
3029
3030bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
3031 bool *ok ) const
3032{
3033 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3034
3035 if ( property )
3036 {
3037 const QVariant value = property->value();
3038
3039 const bool valid = value.canConvert( QVariant::Bool );
3040 if ( ok )
3041 *ok = valid;
3042
3043 if ( valid )
3044 return value.toBool();
3045 }
3046 else if ( ok )
3047 *ok = false;
3048
3049 return def;
3050}
3051
3052bool QgsProject::removeEntry( const QString &scope, const QString &key )
3053{
3054 if ( findKey_( scope, key, mProperties ) )
3055 {
3056 removeKey_( scope, key, mProperties );
3057 setDirty( true );
3058 }
3059
3060 return !findKey_( scope, key, mProperties );
3061}
3062
3063
3064QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
3065{
3066 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3067
3068 QStringList entries;
3069
3070 if ( foundProperty )
3071 {
3072 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3073
3074 if ( propertyKey )
3075 { propertyKey->entryList( entries ); }
3076 }
3077
3078 return entries;
3079}
3080
3081QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
3082{
3083 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3084
3085 QStringList entries;
3086
3087 if ( foundProperty )
3088 {
3089 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3090
3091 if ( propertyKey )
3092 { propertyKey->subkeyList( entries ); }
3093 }
3094
3095 return entries;
3096}
3097
3099{
3100 dump_( mProperties );
3101}
3102
3104{
3105 QString filePath;
3106 switch ( filePathStorage() )
3107 {
3109 break;
3110
3112 {
3113 // for projects stored in a custom storage, we need to ask to the
3114 // storage for the path, if the storage returns an empty path
3115 // relative paths are not supported
3116 if ( QgsProjectStorage *storage = projectStorage() )
3117 {
3118 filePath = storage->filePath( mFile.fileName() );
3119 }
3120 else
3121 {
3122 filePath = fileName();
3123 }
3124 break;
3125 }
3126 }
3127
3128 return QgsPathResolver( filePath, mArchive->dir() );
3129}
3130
3131QString QgsProject::readPath( const QString &src ) const
3132{
3133 return pathResolver().readPath( src );
3134}
3135
3136QString QgsProject::writePath( const QString &src ) const
3137{
3138 return pathResolver().writePath( src );
3139}
3140
3141void QgsProject::setError( const QString &errorMessage )
3142{
3143 mErrorMessage = errorMessage;
3144}
3145
3146QString QgsProject::error() const
3147{
3148 return mErrorMessage;
3149}
3150
3151void QgsProject::clearError()
3152{
3153 setError( QString() );
3154}
3155
3157{
3158 delete mBadLayerHandler;
3159 mBadLayerHandler = handler;
3160}
3161
3162QString QgsProject::layerIsEmbedded( const QString &id ) const
3163{
3164 const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
3165 if ( it == mEmbeddedLayers.constEnd() )
3166 {
3167 return QString();
3168 }
3169 return it.value().first;
3170}
3171
3172bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
3173 bool saveFlag, Qgis::ProjectReadFlags flags )
3174{
3176
3177 static QString sPrevProjectFilePath;
3178 static QDateTime sPrevProjectFileTimestamp;
3179 static QDomDocument sProjectDocument;
3180
3181 QString qgsProjectFile = projectFilePath;
3182 QgsProjectArchive archive;
3183 if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3184 {
3185 archive.unzip( projectFilePath );
3186 qgsProjectFile = archive.projectFile();
3187 }
3188
3189 const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
3190
3191 if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
3192 {
3193 sPrevProjectFilePath.clear();
3194
3195 QFile projectFile( qgsProjectFile );
3196 if ( !projectFile.open( QIODevice::ReadOnly ) )
3197 {
3198 return false;
3199 }
3200
3201 if ( !sProjectDocument.setContent( &projectFile ) )
3202 {
3203 return false;
3204 }
3205
3206 sPrevProjectFilePath = projectFilePath;
3207 sPrevProjectFileTimestamp = projectFileTimestamp;
3208 }
3209
3210 // does project store paths absolute or relative?
3211 bool useAbsolutePaths = true;
3212
3213 const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
3214 if ( !propertiesElem.isNull() )
3215 {
3216 const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
3217 if ( !absElem.isNull() )
3218 {
3219 useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
3220 }
3221 }
3222
3223 QgsReadWriteContext embeddedContext;
3224 if ( !useAbsolutePaths )
3225 embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
3226 embeddedContext.setProjectTranslator( this );
3227 embeddedContext.setTransformContext( transformContext() );
3228
3229 const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
3230 if ( projectLayersElem.isNull() )
3231 {
3232 return false;
3233 }
3234
3235 QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral( "maplayer" ) );
3236 while ( ! mapLayerElem.isNull() )
3237 {
3238 // get layer id
3239 const QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
3240 if ( id == layerId )
3241 {
3242 // layer can be embedded only once
3243 if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
3244 {
3245 return false;
3246 }
3247
3248 mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
3249
3250 if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
3251 {
3252 return true;
3253 }
3254 else
3255 {
3256 mEmbeddedLayers.remove( layerId );
3257 return false;
3258 }
3259 }
3260 mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
3261 }
3262
3263 return false;
3264}
3265
3266
3267QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags )
3268{
3269 QString qgsProjectFile = projectFilePath;
3270 QgsProjectArchive archive;
3271 if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3272 {
3273 archive.unzip( projectFilePath );
3274 qgsProjectFile = archive.projectFile();
3275 }
3276
3277 // open project file, get layer ids in group, add the layers
3278 QFile projectFile( qgsProjectFile );
3279 if ( !projectFile.open( QIODevice::ReadOnly ) )
3280 {
3281 return nullptr;
3282 }
3283
3284 QDomDocument projectDocument;
3285 if ( !projectDocument.setContent( &projectFile ) )
3286 {
3287 return nullptr;
3288 }
3289
3290 QgsReadWriteContext context;
3291 context.setPathResolver( pathResolver() );
3292 context.setProjectTranslator( this );
3294
3296
3297 QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
3298 if ( !layerTreeElem.isNull() )
3299 {
3300 root->readChildrenFromXml( layerTreeElem, context );
3301 }
3302 else
3303 {
3304 QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
3305 }
3306
3307 QgsLayerTreeGroup *group = root->findGroup( groupName );
3308 if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
3309 {
3310 // embedded groups cannot be embedded again
3311 delete root;
3312 return nullptr;
3313 }
3314
3315 // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
3316 QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
3317 delete root;
3318 root = nullptr;
3319
3320 newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3321 newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
3322
3323 // set "embedded" to all children + load embedded layers
3324 mLayerTreeRegistryBridge->setEnabled( false );
3325 initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
3326 mLayerTreeRegistryBridge->setEnabled( true );
3327
3328 // consider the layers might be identify disabled in its project
3329 const auto constFindLayerIds = newGroup->findLayerIds();
3330 for ( const QString &layerId : constFindLayerIds )
3331 {
3332 QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
3333 if ( layer )
3334 {
3335 layer->resolveReferences( this );
3336 layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
3337 }
3338 }
3339
3340 return newGroup;
3341}
3342
3343void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
3344{
3345 const auto constChildren = group->children();
3346 for ( QgsLayerTreeNode *child : constChildren )
3347 {
3348 // all nodes in the subtree will have "embedded" custom property set
3349 child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3350
3351 if ( QgsLayerTree::isGroup( child ) )
3352 {
3353 initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
3354 }
3355 else if ( QgsLayerTree::isLayer( child ) )
3356 {
3357 // load the layer into our project
3358 QList<QDomNode> brokenNodes;
3359 createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
3360 }
3361 }
3362}
3363
3368
3373
3375{
3376 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
3378}
3379
3381{
3382 return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
3383}
3384
3386{
3387 if ( mDistanceUnits == unit )
3388 return;
3389
3390 mDistanceUnits = unit;
3391
3392 emit distanceUnitsChanged();
3393}
3394
3396{
3397 if ( mAreaUnits == unit )
3398 return;
3399
3400 mAreaUnits = unit;
3401
3402 emit areaUnitsChanged();
3403}
3404
3406{
3407 if ( !mCachedHomePath.isEmpty() )
3408 return mCachedHomePath;
3409
3410 const QFileInfo pfi( fileName() );
3411
3412 if ( !mHomePath.isEmpty() )
3413 {
3414 const QFileInfo homeInfo( mHomePath );
3415 if ( !homeInfo.isRelative() )
3416 {
3417 mCachedHomePath = mHomePath;
3418 return mHomePath;
3419 }
3420 }
3421 else if ( !fileName().isEmpty() )
3422 {
3423
3424 // If it's not stored in the file system, try to get the path from the storage
3425 if ( QgsProjectStorage *storage = projectStorage() )
3426 {
3427 const QString storagePath { storage->filePath( fileName() ) };
3428 if ( ! storagePath.isEmpty() && QFileInfo::exists( storagePath ) )
3429 {
3430 mCachedHomePath = QFileInfo( storagePath ).path();
3431 return mCachedHomePath;
3432 }
3433 }
3434
3435 mCachedHomePath = pfi.path();
3436 return mCachedHomePath;
3437 }
3438
3439 if ( !pfi.exists() )
3440 {
3441 mCachedHomePath = mHomePath;
3442 return mHomePath;
3443 }
3444
3445 if ( !mHomePath.isEmpty() )
3446 {
3447 // path is relative to project file
3448 mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
3449 }
3450 else
3451 {
3452 mCachedHomePath = pfi.canonicalPath();
3453 }
3454 return mCachedHomePath;
3455}
3456
3458{
3459 return mHomePath;
3460}
3461
3463{
3464 return mRelationManager;
3465}
3466
3468{
3469 return mLayoutManager.get();
3470}
3471
3473{
3474 return mLayoutManager.get();
3475}
3476
3478{
3479 return m3DViewsManager.get();
3480}
3481
3483{
3484 return m3DViewsManager.get();
3485}
3486
3488{
3489 return mBookmarkManager;
3490}
3491
3493{
3494 return mBookmarkManager;
3495}
3496
3498{
3499 return mViewSettings;
3500}
3501
3503{
3504 return mViewSettings;
3505}
3506
3508{
3509 return mStyleSettings;
3510}
3511
3513{
3514 return mStyleSettings;
3515}
3516
3518{
3519 return mTimeSettings;
3520}
3521
3523{
3524 return mTimeSettings;
3525}
3526
3528{
3529 return mElevationProperties;
3530}
3531
3533{
3534 return mElevationProperties;
3535}
3536
3538{
3539 return mDisplaySettings;
3540}
3541
3543{
3544 return mDisplaySettings;
3545}
3546
3548{
3549 return mRootGroup;
3550}
3551
3553{
3554 return mMapThemeCollection.get();
3555}
3556
3558{
3559 return mAnnotationManager.get();
3560}
3561
3563{
3564 return mAnnotationManager.get();
3565}
3566
3567void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
3568{
3569 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3570 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3571 {
3572 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3573 continue;
3574
3575 if ( layers.contains( it.value() ) )
3576 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
3577 else
3578 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
3579 }
3580
3584}
3585
3586void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
3587{
3588 QList<QgsMapLayer *> nonIdentifiableLayers;
3589 nonIdentifiableLayers.reserve( layerIds.count() );
3590 for ( const QString &layerId : layerIds )
3591 {
3592 QgsMapLayer *layer = mapLayer( layerId );
3593 if ( layer )
3594 nonIdentifiableLayers << layer;
3595 }
3599}
3600
3602{
3603 QStringList nonIdentifiableLayers;
3604
3605 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3606 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3607 {
3608 if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3609 {
3610 nonIdentifiableLayers.append( it.value()->id() );
3611 }
3612 }
3613 return nonIdentifiableLayers;
3614}
3615
3617{
3618 return mTransactionMode == Qgis::TransactionMode::AutomaticGroups;
3619}
3620
3621void QgsProject::setAutoTransaction( bool autoTransaction )
3622{
3623 if ( autoTransaction
3624 && mTransactionMode == Qgis::TransactionMode::AutomaticGroups )
3625 return;
3626
3627 if ( ! autoTransaction
3628 && mTransactionMode == Qgis::TransactionMode::Disabled )
3629 return;
3630
3631 if ( autoTransaction )
3632 mTransactionMode = Qgis::TransactionMode::AutomaticGroups;
3633 else
3634 mTransactionMode = Qgis::TransactionMode::Disabled;
3635
3636 updateTransactionGroups();
3637}
3638
3640{
3641 return mTransactionMode;
3642}
3643
3645{
3646 if ( transactionMode == mTransactionMode )
3647 return true;
3648
3649 // Check that all layer are not in edit mode
3650 const auto constLayers = mapLayers().values();
3651 for ( QgsMapLayer *layer : constLayers )
3652 {
3653 if ( layer->isEditable() )
3654 {
3655 QgsLogger::warning( tr( "Transaction mode can be changed only if all layers are not editable." ) );
3656 return false;
3657 }
3658 }
3659
3660 mTransactionMode = transactionMode;
3661 updateTransactionGroups();
3662 return true;
3663}
3664
3665QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
3666{
3667 return mTransactionGroups;
3668}
3669
3670
3671//
3672// QgsMapLayerStore methods
3673//
3674
3675
3677{
3678 return mLayerStore->count();
3679}
3680
3682{
3683 return mLayerStore->validCount();
3684}
3685
3686QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
3687{
3688 return mLayerStore->mapLayer( layerId );
3689}
3690
3691QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
3692{
3693 return mLayerStore->mapLayersByName( layerName );
3694}
3695
3696QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
3697{
3698 QList<QgsMapLayer *> layers;
3699 const auto constMapLayers { mLayerStore->mapLayers() };
3700 for ( const auto &l : constMapLayers )
3701 {
3702 if ( ! l->shortName().isEmpty() )
3703 {
3704 if ( l->shortName() == shortName )
3705 layers << l;
3706 }
3707 else if ( l->name() == shortName )
3708 {
3709 layers << l;
3710 }
3711 }
3712 return layers;
3713}
3714
3715bool QgsProject::unzip( const QString &filename, Qgis::ProjectReadFlags flags )
3716{
3717 clearError();
3718 std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3719
3720 // unzip the archive
3721 if ( !archive->unzip( filename ) )
3722 {
3723 setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
3724 return false;
3725 }
3726
3727 // test if zip provides a .qgs file
3728 if ( archive->projectFile().isEmpty() )
3729 {
3730 setError( tr( "Zip archive does not provide a project file" ) );
3731 return false;
3732 }
3733
3734 // Keep the archive
3735 mArchive = std::move( archive );
3736
3737 // load auxiliary storage
3738 if ( !static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile().isEmpty() )
3739 {
3740 // database file is already a copy as it's been unzipped. So we don't open
3741 // auxiliary storage in copy mode in this case
3742 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile(), false ) );
3743 }
3744 else
3745 {
3746 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
3747 }
3748
3749 // read the project file
3750 if ( ! readProjectFile( static_cast<QgsProjectArchive *>( mArchive.get() )->projectFile(), flags ) )
3751 {
3752 setError( tr( "Cannot read unzipped qgs project file" ) + QStringLiteral( ": " ) + error() );
3753 return false;
3754 }
3755
3756 // Remove the temporary .qgs file
3757 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3758
3759 return true;
3760}
3761
3762bool QgsProject::zip( const QString &filename )
3763{
3764 clearError();
3765
3766 // save the current project in a temporary .qgs file
3767 std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3768 const QString baseName = QFileInfo( filename ).baseName();
3769 const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
3770 QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
3771
3772 bool writeOk = false;
3773 if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3774 {
3775 writeOk = writeProjectFile( qgsFile.fileName() );
3776 qgsFile.close();
3777 }
3778
3779 // stop here with an error message
3780 if ( ! writeOk )
3781 {
3782 setError( tr( "Unable to write temporary qgs file" ) );
3783 return false;
3784 }
3785
3786 // save auxiliary storage
3787 const QFileInfo info( qgsFile );
3788 const QString asExt = QStringLiteral( ".%1" ).arg( QgsAuxiliaryStorage::extension() );
3789 const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
3790
3791 bool auxiliaryStorageSavedOk = true;
3792 if ( ! saveAuxiliaryStorage( asFileName ) )
3793 {
3794 const QString err = mAuxiliaryStorage->errorString();
3795 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 ) );
3796 auxiliaryStorageSavedOk = false;
3797
3798 // fixes the current archive and keep the previous version of qgd
3799 if ( !mArchive->exists() )
3800 {
3801 mArchive.reset( new QgsProjectArchive() );
3802 mArchive->unzip( mFile.fileName() );
3803 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3804
3805 const QString auxiliaryStorageFile = static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile();
3806 if ( ! auxiliaryStorageFile.isEmpty() )
3807 {
3808 archive->addFile( auxiliaryStorageFile );
3809 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( auxiliaryStorageFile, false ) );
3810 }
3811 }
3812 }
3813 else
3814 {
3815 // in this case, an empty filename means that the auxiliary database is
3816 // empty, so we don't want to save it
3817 if ( QFile::exists( asFileName ) )
3818 {
3819 archive->addFile( asFileName );
3820 }
3821 }
3822
3823 // create the archive
3824 archive->addFile( qgsFile.fileName() );
3825
3826 // Add all other files
3827 const QStringList &files = mArchive->files();
3828 for ( const QString &file : files )
3829 {
3830 if ( !file.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
3831 {
3832 archive->addFile( file );
3833 }
3834 }
3835
3836 // zip
3837 bool zipOk = true;
3838 if ( !archive->zip( filename ) )
3839 {
3840 setError( tr( "Unable to perform zip" ) );
3841 zipOk = false;
3842 }
3843
3844 return auxiliaryStorageSavedOk && zipOk;
3845}
3846
3848{
3849 return QgsZipUtils::isZipFile( mFile.fileName() );
3850}
3851
3852QList<QgsMapLayer *> QgsProject::addMapLayers(
3853 const QList<QgsMapLayer *> &layers,
3854 bool addToLegend,
3855 bool takeOwnership )
3856{
3857 const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
3858 if ( !myResultList.isEmpty() )
3859 {
3860 // Update transform context
3861 for ( auto &l : myResultList )
3862 {
3863 l->setTransformContext( transformContext() );
3864 }
3865 if ( addToLegend )
3866 {
3867 emit legendLayersAdded( myResultList );
3868 }
3869 }
3870
3871 if ( mAuxiliaryStorage )
3872 {
3873 for ( QgsMapLayer *mlayer : myResultList )
3874 {
3875 if ( mlayer->type() != QgsMapLayerType::VectorLayer )
3876 continue;
3877
3878 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
3879 if ( vl )
3880 {
3881 vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
3882 }
3883 }
3884 }
3885
3886 mProjectScope.reset();
3887
3888 return myResultList;
3889}
3890
3893 bool addToLegend,
3894 bool takeOwnership )
3895{
3896 QList<QgsMapLayer *> addedLayers;
3897 addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
3898 return addedLayers.isEmpty() ? nullptr : addedLayers[0];
3899}
3900
3901void QgsProject::removeAuxiliaryLayer( const QgsMapLayer *ml )
3902{
3903 if ( ! ml || ml->type() != QgsMapLayerType::VectorLayer )
3904 return;
3905
3906 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( ml );
3907 if ( vl && vl->auxiliaryLayer() )
3908 {
3909 const QgsDataSourceUri uri( vl->auxiliaryLayer()->source() );
3911 }
3912}
3913
3914void QgsProject::removeMapLayers( const QStringList &layerIds )
3915{
3916 for ( const auto &layerId : layerIds )
3917 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
3918
3919 mProjectScope.reset();
3920 mLayerStore->removeMapLayers( layerIds );
3921}
3922
3923void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3924{
3925 for ( const auto &layer : layers )
3926 removeAuxiliaryLayer( layer );
3927
3928 mProjectScope.reset();
3929 mLayerStore->removeMapLayers( layers );
3930}
3931
3932void QgsProject::removeMapLayer( const QString &layerId )
3933{
3934 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
3935 mProjectScope.reset();
3936 mLayerStore->removeMapLayer( layerId );
3937}
3938
3940{
3941 removeAuxiliaryLayer( layer );
3942 mProjectScope.reset();
3943 mLayerStore->removeMapLayer( layer );
3944}
3945
3947{
3948 mProjectScope.reset();
3949 return mLayerStore->takeMapLayer( layer );
3950}
3951
3953{
3954 return mMainAnnotationLayer;
3955}
3956
3958{
3959 if ( mLayerStore->count() == 0 )
3960 return;
3961
3962 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
3963 mProjectScope.reset();
3964 mLayerStore->removeAllMapLayers();
3965
3966 snapSingleBlocker.release();
3967 mSnappingConfig.clearIndividualLayerSettings();
3968 if ( !mBlockSnappingUpdates )
3969 emit snappingConfigChanged( mSnappingConfig );
3970}
3971
3973{
3974 const QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3975 QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3976 for ( ; it != layers.constEnd(); ++it )
3977 {
3978 it.value()->reload();
3979 }
3980}
3981
3982QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3983{
3984 return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3985}
3986
3987QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3988{
3989 return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3990}
3991
3993{
3994 return &mEditBufferGroup;
3995}
3996
3998{
4000
4001 // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
4002 // 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)
4003 if ( mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
4004 || mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == QLatin1String( "2" ) )
4005 {
4006 // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
4007 defaultCrs = crs();
4008 }
4009 else
4010 {
4011 // global crs
4012 const QString layerDefaultCrs = mSettings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
4013 defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
4014 }
4015
4016 return defaultCrs;
4017}
4018
4023
4028
4029bool QgsProject::saveAuxiliaryStorage( const QString &filename )
4030{
4031 const QMap<QString, QgsMapLayer *> layers = mapLayers();
4032 bool empty = true;
4033 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4034 {
4035 if ( it.value()->type() != QgsMapLayerType::VectorLayer )
4036 continue;
4037
4038 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
4039 if ( vl && vl->auxiliaryLayer() )
4040 {
4041 vl->auxiliaryLayer()->save();
4042 empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
4043 }
4044 }
4045
4046 if ( !mAuxiliaryStorage->exists( *this ) && empty )
4047 {
4048 return true; // it's not an error
4049 }
4050 else if ( !filename.isEmpty() )
4051 {
4052 return mAuxiliaryStorage->saveAs( filename );
4053 }
4054 else
4055 {
4056 return mAuxiliaryStorage->saveAs( *this );
4057 }
4058}
4059
4060QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
4061{
4062 static QgsPropertiesDefinition sPropertyDefinitions
4063 {
4064 {
4066 QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
4067 },
4068 };
4069 return sPropertyDefinitions;
4070}
4071
4073{
4074 return mAuxiliaryStorage.get();
4075}
4076
4078{
4079 return mAuxiliaryStorage.get();
4080}
4081
4082QString QgsProject::createAttachedFile( const QString &nameTemplate )
4083{
4084 const QDir archiveDir( mArchive->dir() );
4085 QTemporaryFile tmpFile( archiveDir.filePath( "XXXXXX_" + nameTemplate ), this );
4086 tmpFile.setAutoRemove( false );
4087 tmpFile.open();
4088 mArchive->addFile( tmpFile.fileName() );
4089 return tmpFile.fileName();
4090}
4091
4092QStringList QgsProject::attachedFiles() const
4093{
4094 QStringList attachments;
4095 const QString baseName = QFileInfo( fileName() ).baseName();
4096 const QStringList files = mArchive->files();
4097 attachments.reserve( files.size() );
4098 for ( const QString &file : files )
4099 {
4100 if ( QFileInfo( file ).baseName() != baseName )
4101 {
4102 attachments.append( file );
4103 }
4104 }
4105 return attachments;
4106}
4107
4108bool QgsProject::removeAttachedFile( const QString &path )
4109{
4110 return mArchive->removeFile( path );
4111}
4112
4113QString QgsProject::attachmentIdentifier( const QString &attachedFile ) const
4114{
4115 return QStringLiteral( "attachment:///%1" ).arg( QFileInfo( attachedFile ).fileName() );
4116}
4117
4118QString QgsProject::resolveAttachmentIdentifier( const QString &identifier ) const
4119{
4120 if ( identifier.startsWith( QLatin1String( "attachment:///" ) ) )
4121 {
4122 return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
4123 }
4124 return QString();
4125}
4126
4128{
4129 return mMetadata;
4130}
4131
4133{
4134 if ( metadata == mMetadata )
4135 return;
4136
4137 mMetadata = metadata;
4138 mProjectScope.reset();
4139
4140 emit metadataChanged();
4141
4142 setDirty( true );
4143}
4144
4145QSet<QgsMapLayer *> QgsProject::requiredLayers() const
4146{
4147 QSet<QgsMapLayer *> requiredLayers;
4148
4149 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4150 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4151 {
4152 if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4153 {
4154 requiredLayers.insert( it.value() );
4155 }
4156 }
4157 return requiredLayers;
4158}
4159
4160void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
4161{
4162 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4163 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4164 {
4165 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4166 continue;
4167
4168 if ( layers.contains( it.value() ) )
4169 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
4170 else
4171 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
4172 }
4173}
4174
4176{
4177 // save colors to project
4178 QStringList customColors;
4179 QStringList customColorLabels;
4180
4181 QgsNamedColorList::const_iterator colorIt = colors.constBegin();
4182 for ( ; colorIt != colors.constEnd(); ++colorIt )
4183 {
4184 const QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
4185 const QString label = ( *colorIt ).second;
4186 customColors.append( color );
4187 customColorLabels.append( label );
4188 }
4189 writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
4190 writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
4191 mProjectScope.reset();
4192 emit projectColorsChanged();
4193}
4194
4195void QgsProject::setBackgroundColor( const QColor &color )
4196{
4197 if ( mBackgroundColor == color )
4198 return;
4199
4200 mBackgroundColor = color;
4202}
4203
4205{
4206 return mBackgroundColor;
4207}
4208
4209void QgsProject::setSelectionColor( const QColor &color )
4210{
4211 if ( mSelectionColor == color )
4212 return;
4213
4214 mSelectionColor = color;
4215 emit selectionColorChanged();
4216}
4217
4219{
4220 return mSelectionColor;
4221}
4222
4223void QgsProject::setMapScales( const QVector<double> &scales )
4224{
4225 mViewSettings->setMapScales( scales );
4226}
4227
4228QVector<double> QgsProject::mapScales() const
4229{
4230 return mViewSettings->mapScales();
4231}
4232
4234{
4235 mViewSettings->setUseProjectScales( enabled );
4236}
4237
4239{
4240 return mViewSettings->useProjectScales();
4241}
4242
4243void QgsProject::generateTsFile( const QString &locale )
4244{
4245 QgsTranslationContext translationContext;
4246 translationContext.setProject( this );
4247 translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
4248
4249 QgsApplication::instance()->collectTranslatableObjects( &translationContext );
4250
4251 translationContext.writeTsFile( locale );
4252}
4253
4254QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
4255{
4256 if ( !mTranslator )
4257 {
4258 return sourceText;
4259 }
4260
4261 QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
4262
4263 if ( result.isEmpty() )
4264 {
4265 return sourceText;
4266 }
4267 return result;
4268}
4269
4271{
4272 const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
4273 if ( !layers.empty() )
4274 {
4275 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4276 {
4277 // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
4278 if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4279 {
4280 if ( !( ( *it )->accept( visitor ) ) )
4281 return false;
4282
4283 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4284 return false;
4285 }
4286 }
4287 }
4288
4289 if ( !mLayoutManager->accept( visitor ) )
4290 return false;
4291
4292 if ( !mAnnotationManager->accept( visitor ) )
4293 return false;
4294
4295 return true;
4296}
4297
4298void QgsProject::loadProjectFlags( const QDomDocument *doc )
4299{
4300 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectFlags" ) );
4301 Qgis::ProjectFlags flags;
4302 if ( !element.isNull() )
4303 {
4304 flags = qgsFlagKeysToValue( element.attribute( QStringLiteral( "set" ) ), Qgis::ProjectFlags() );
4305 }
4306 else
4307 {
4308 // older project compatibility
4309 element = doc->documentElement().firstChildElement( QStringLiteral( "evaluateDefaultValues" ) );
4310 if ( !element.isNull() )
4311 {
4312 if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
4314 }
4315
4316 // Read trust layer metadata config in the project
4317 element = doc->documentElement().firstChildElement( QStringLiteral( "trust" ) );
4318 if ( !element.isNull() )
4319 {
4320 if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
4322 }
4323 }
4324
4325 setFlags( flags );
4326}
4327
4329GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
4330 : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
4331{
4332 if ( !project )
4333 return;
4334
4335 //build up color list from project. Do this in advance for speed
4336 QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
4337 const QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
4338
4339 //generate list from custom colors
4340 int colorIndex = 0;
4341 for ( QStringList::iterator it = colorStrings.begin();
4342 it != colorStrings.end(); ++it )
4343 {
4344 const QColor color = QgsSymbolLayerUtils::decodeColor( *it );
4345 QString label;
4346 if ( colorLabels.length() > colorIndex )
4347 {
4348 label = colorLabels.at( colorIndex );
4349 }
4350
4351 mColors.insert( label.toLower(), color );
4352 colorIndex++;
4353 }
4354}
4355
4356GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
4357 : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
4358 , mColors( colors )
4359{
4360}
4361
4362QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
4363{
4364 const QString colorName = values.at( 0 ).toString().toLower();
4365 if ( mColors.contains( colorName ) )
4366 {
4367 return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
4368 }
4369 else
4370 return QVariant();
4371}
4372
4373QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
4374{
4375 return new GetNamedProjectColor( mColors );
4376}
@ DontLoad3DViews
Skip loading 3D views (since QGIS 3.26)
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
@ ForceReadOnlyLayers
Open layers in a read-only mode. (since QGIS 3.28)
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
@ DontResolveLayers
Don't resolve layer paths (i.e. don't load any layer content). Dramatically improves project read tim...
static QString version()
Version string.
Definition qgis.cpp:277
FilePathType
File path types.
Definition qgis.h:772
@ Relative
Relative path.
@ Absolute
Absolute path.
TransactionMode
Transaction mode.
Definition qgis.h:1914
@ 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.
@ Critical
Critical/error message.
Definition qgis.h:118
@ Success
Used for reporting a successful operation.
Definition qgis.h:119
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
Definition qgis.h:2085
@ 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:1978
@ 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.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
This is an abstract base class for any elements of a drag and drop form.
QString name() const
Returns the name of this element.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
bool save()
Commits changes and starts editing then.
Class providing some utility methods to manage auxiliary storage.
static QString extension()
Returns the extension used for auxiliary databases.
static bool deleteTable(const QgsDataSourceUri &uri)
Removes a table from the auxiliary storage.
Manages storage of a set of bookmarks.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all bookmarks present in the XML document.
void clear()
Removes and deletes all bookmarks from the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
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.
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 ...
Class for storing the component parts of a RDBMS data source URI (e.g.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
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:672
Container of fields for a vector layer.
Definition qgsfields.h:45
bool isEmpty() const
Checks whether the container is empty.
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.
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
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.
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.
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
@ Removable
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
@ FlagForceReadOnly
Force open as read only.
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
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.
QString auxiliaryStorageFile() const
Returns the current .qgd auxiliary storage file or an empty string if there's none.
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
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.
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.
~QgsProject() override
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:202
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project's associated file.
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.
QColor selectionColor
Definition qgsproject.h:119
QString title() const
Returns the project's title.
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...
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Qgis::FilePathType filePathStorage() const
Returns the type of paths used when storing file paths in a QGS/QGZ project file.
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.
QgsUnitTypes::DistanceUnit distanceUnits
Definition qgsproject.h:121
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.
void areaUnitsChanged()
Emitted when the default area units changes.
QgsPropertyCollection dataDefinedServerProperties() const
Returns the data defined properties used for overrides in user defined server parameters.
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.
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.
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.
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
void distanceUnitsChanged()
Emitted when the default distance units changes.
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
const QgsBookmarkManager * bookmarkManager() const
Returns the project's bookmark manager, which manages bookmarks within the project.
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted after the basic initialization of a layer from the project file is done.
Q_DECL_DEPRECATED void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
bool startEditing(QgsVectorLayer *vectorLayer=nullptr)
Makes the layer editable.
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...
@ WMSOnlineResource
Alias.
Definition qgsproject.h:139
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.
QgsUnitTypes::AreaUnit areaUnits
Definition qgsproject.h:122
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 ...
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...
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.
QgsProjectDisplaySettings * displaySettings
Definition qgsproject.h:123
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.
QString saveUser() const
Returns the user name that did the last save.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
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.
void setOriginalPath(const QString &path)
Sets the original path associated with the project.
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.
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.
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.
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
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...
QDateTime lastSaveDateTime() const
Returns the date and time when the project was last saved.
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".
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
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).
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.
void setFilePathStorage(Qgis::FilePathType type)
Sets the type of paths used when storing file paths in a QGS/QGZ project file.
Q_DECL_DEPRECATED QSet< QgsMapLayer * > requiredLayers() const
Returns a set of map layers that are required in the project and therefore they should not get remove...
void transformContextChanged()
Emitted when the project transformContext() is changed.
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
void legendLayersAdded(const QList< QgsMapLayer * > &layers)
Emitted, when a layer was added to the registry and the legend.
QVariantMap customVariables() const
A map of custom project variables.
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer * > &layers)
Sets the list of layers with which intersections should be avoided.
void homePathChanged()
Emitted when the home path of the project changes.
void dirtySet()
Emitted when setDirty(true) is called.
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const
Returns the default CRS for new layers based on the settings and the current project CRS.
QString saveUserFullName() const
Returns the full user name that did the last save.
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()
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.
void setFlags(Qgis::ProjectFlags flags)
Sets the project's flags, which dictate the behavior of the project.
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.
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.
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:46
@ String
Any string value.
Definition qgsproperty.h:60
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.
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.
@ DistanceMeters
Meters.
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.
@ AreaSquareMeters
Square meters.
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.
CORE_EXPORT const QStringList files(const QString &zip)
Returns the list of files within a zip file.
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition qgis.h:2979
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:2700
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:3061
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:2681
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition qgis.h:2739
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:2761
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:3060
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition qgis.h:2973
const QgsField & field
Definition qgsfield.h:476
#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.
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.
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...
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.
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value, bool &propertiesModified)
Adds the given key and value.
CORE_EXPORT QgsProjectVersion getVersion(QDomDocument const &doc)
Returns the version string found in the given DOM document.
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.
Contains information relating to a node (i.e.