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