19#include "moc_qgsproject.cpp"
81#include <QApplication>
86#include <QTemporaryFile>
89#include <QStandardPaths>
91#include <QRegularExpression>
113 QStringList keyTokens = QStringList( scope );
114 keyTokens += key.split(
'/', Qt::SkipEmptyParts );
117 keyTokens.push_front( QStringLiteral(
"properties" ) );
120 for (
int i = 0; i < keyTokens.size(); ++i )
122 const QString keyToken = keyTokens.at( i );
126 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}])" ) );
127 if ( keyToken.contains( sInvalidRegexp ) )
129 const QString errorString = QObject::tr(
"Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
157 while ( !keySequence.isEmpty() )
161 if ( keySequence.first() == currentProperty->
name() )
164 keySequence.pop_front();
166 if ( 1 == keySequence.count() )
169 return currentProperty->
find( keySequence.front() );
171 else if ( keySequence.isEmpty() )
176 return currentProperty;
178 else if ( ( nextProperty = currentProperty->
find( keySequence.first() ) ) )
180 if ( nextProperty->
isKey() )
184 else if ( nextProperty->
isValue() && 1 == keySequence.count() )
190 return currentProperty;
228 const QVariant &value,
229 bool &propertiesModified )
238 propertiesModified =
false;
239 while ( ! keySequence.isEmpty() )
243 if ( keySequence.first() == currentProperty->
name() )
246 keySequence.pop_front();
250 if ( 1 == keySequence.count() )
253 if ( !property || property->value() != value )
255 currentProperty->
setValue( keySequence.front(), value );
256 propertiesModified =
true;
259 return currentProperty;
263 else if ( keySequence.isEmpty() )
265 if ( currentProperty->
value() != value )
268 propertiesModified =
true;
271 return currentProperty;
273 else if ( ( nextProperty = currentProperty->
find( keySequence.first() ) ) )
277 if ( currentProperty )
288 if ( ( newPropertyKey = currentProperty->
addKey( keySequence.first() ) ) )
290 currentProperty = newPropertyKey;
322 while ( ! keySequence.isEmpty() )
326 if ( keySequence.first() == currentProperty->
name() )
329 keySequence.pop_front();
333 if ( 1 == keySequence.count() )
335 currentProperty->
removeKey( keySequence.front() );
340 else if ( keySequence.isEmpty() )
342 previousQgsPropertyKey->
removeKey( currentProperty->
name() );
344 else if ( ( nextProperty = currentProperty->
find( keySequence.first() ) ) )
346 previousQgsPropertyKey = currentProperty;
349 if ( currentProperty )
373 , mCapabilities( capabilities )
376 , mSnappingConfig( this )
394 mProperties.
setName( QStringLiteral(
"properties" ) );
397 mMainAnnotationLayer->setParent(
this );
411 this, [
this](
const QStringList &
layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
413 this, [
this](
const QList<QgsMapLayer *> &
layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
415 this, [
this](
const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
417 this, [
this](
QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
419 [
this](
const QStringList &
layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
421 [
this](
const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
423 [
this]() { mProjectScope.reset(); emit removeAll(); } );
425 [
this](
const QList< QgsMapLayer * > &
layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
427 [
this](
QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
435 [
this](
const QList<QgsMapLayer *> &
layers )
437 for ( const auto &layer : layers )
439 disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
444 [
this](
const QList<QgsMapLayer *> &layers )
446 for ( const auto &layer : layers )
448 connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
457 mStyleSettings->combinedStyleModel()->addDefaultStyle();
463 mIsBeingDeleted =
true;
466 releaseHandlesToProjectArchive();
467 delete mBadLayerHandler;
468 delete mRelationManager;
469 delete mLayerTreeRegistryBridge;
471 if (
this == sProject )
502 mProjectScope.reset();
513 return mMetadata.
title();
522 if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
525 for (
auto layerIt =
layers.constBegin(); layerIt !=
layers.constEnd(); ++layerIt )
527 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
528 if ( vl->dataProvider() )
535 if ( oldTrustLayerMetadata != newTrustLayerMetadata )
538 for (
auto layerIt =
layers.constBegin(); layerIt !=
layers.constEnd(); ++layerIt )
540 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
542 vl->setReadExtentFromXml( newTrustLayerMetadata );
547 if ( mFlags !=
flags )
562 newFlags &= ~(
static_cast< int >( flag ) );
577 return mSaveUserFull;
584 return mSaveDateTime;
605 if ( dirty && mDirtyBlockCount > 0 )
611 if ( mDirty == dirty )
622 if ( path == mHomePath )
626 mCachedHomePath.clear();
627 mProjectScope.reset();
638 const QList<QgsAttributeEditorElement *> elements = parent->
children();
646 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1:formcontainers" ).arg( layerId ), container->
name() );
648 if ( !container->
children().empty() )
663 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1" ).arg( layer->layerId() ), layer->name() );
672 for (
const QgsField &field : fields )
675 if ( field.alias().isEmpty() )
676 fieldName = field.name();
678 fieldName = field.alias();
680 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1:fieldaliases" ).arg( vlayer->
id() ), fieldName );
682 if ( field.editorWidgetSetup().type() == QLatin1String(
"ValueRelation" ) )
684 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->
id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral(
"Value" ) ).toString() );
695 const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->
findGroups();
698 translationContext->
registerTranslation( QStringLiteral(
"project:layergroups" ), groupLayer->name() );
702 const QList<QgsRelation> &relations = mRelationManager->
relations().values();
705 translationContext->
registerTranslation( QStringLiteral(
"project:relations" ), relation.name() );
713 mDataDefinedServerProperties = properties;
720 return mDataDefinedServerProperties;
727 switch ( mTransactionMode )
748 switch ( mTransactionMode )
755 commitErrors.append( tr(
"Trying to commit changes without a layer specified. This only works if the transaction mode is buffered" ) );
764 return mEditBufferGroup.
commitChanges( commitErrors, stopEditing );
774 switch ( mTransactionMode )
781 rollbackErrors.append( tr(
"Trying to roll back changes without a layer specified. This only works if the transaction mode is buffered" ) );
784 bool success = vectorLayer->
rollBack( stopEditing );
790 return mEditBufferGroup.
rollBack( rollbackErrors, stopEditing );
800 if ( name == mFile.fileName() )
803 const QString oldHomePath =
homePath();
805 mFile.setFileName( name );
806 mCachedHomePath.clear();
807 mProjectScope.reset();
811 const QString newHomePath =
homePath();
812 if ( newHomePath != oldHomePath )
823 return mFile.fileName();
830 mOriginalPath = path;
837 return mOriginalPath;
844 return QFileInfo( mFile );
862 storage->readProjectStorageMetadata( mFile.fileName(),
metadata );
867 return QFileInfo( mFile.fileName() ).lastModified();
878 if ( mFile.fileName().isEmpty() )
881 return QFileInfo( mFile.fileName() ).absolutePath();
892 if ( mFile.fileName().isEmpty() )
895 return QFileInfo( mFile.fileName() ).absoluteFilePath();
906 storage->readProjectStorageMetadata( mFile.fileName(),
metadata );
911 return QFileInfo( mFile.fileName() ).completeBaseName();
919 const bool absolutePaths =
readBoolEntry( QStringLiteral(
"Paths" ), QStringLiteral(
"/Absolute" ),
false );
930 writeEntry( QStringLiteral(
"Paths" ), QStringLiteral(
"/Absolute" ),
true );
933 writeEntry( QStringLiteral(
"Paths" ), QStringLiteral(
"/Absolute" ),
false );
950 return mCrs3D.
isValid() ? mCrs3D : mCrs;
962 writeEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectionsEnabled" ),
crs.
isValid() ? 1 : 0 );
963 mProjectScope.reset();
977 if ( oldCrs3D != mCrs3D )
981 if ( adjustEllipsoid )
990 if ( !
crs().isValid() )
993 return readEntry( QStringLiteral(
"Measure" ), QStringLiteral(
"/Ellipsoid" ),
geoNone() );
1000 if (
ellipsoid ==
readEntry( QStringLiteral(
"Measure" ), QStringLiteral(
"/Ellipsoid" ) ) )
1003 mProjectScope.reset();
1013 switch ( mCrs.
type() )
1016 QgsDebugError( QStringLiteral(
"Project has a vertical CRS set as the horizontal CRS!" ) );
1035 return mVerticalCrs;
1063 *errorMessage = QObject::tr(
"Specified CRS is a %1 CRS, not a Vertical CRS" ).arg(
qgsEnumValueToKey(
crs.
type() ) );
1068 if (
crs != mVerticalCrs )
1073 switch ( mCrs.
type() )
1076 if (
crs != oldVerticalCrs )
1079 *errorMessage = QObject::tr(
"Project CRS is a Compound CRS, specified Vertical CRS will be ignored" );
1085 if (
crs != oldVerticalCrs )
1088 *errorMessage = QObject::tr(
"Project CRS is a Geographic 3D CRS, specified Vertical CRS will be ignored" );
1094 if (
crs != oldVerticalCrs )
1097 *errorMessage = QObject::tr(
"Project CRS is a Geocentric CRS, specified Vertical CRS will be ignored" );
1106 *errorMessage = QObject::tr(
"Project CRS is a Projected 3D CRS, specified Vertical CRS will be ignored" );
1124 res = rebuildCrs3D( errorMessage );
1125 mProjectScope.reset();
1132 if ( mCrs3D != oldCrs3D )
1143 return mTransformContext;
1150 if ( context == mTransformContext )
1153 mTransformContext = context;
1154 mProjectScope.reset();
1157 for (
auto &layer : mLayerStore.get()->mapLayers() )
1159 layer->setTransformContext( context );
1168 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
1172 if ( !mIsBeingDeleted )
1181 mProjectScope.reset();
1182 mFile.setFileName( QString() );
1185 mSaveUserFull.clear();
1186 mSaveDateTime = QDateTime();
1189 mCachedHomePath.clear();
1193 mCustomVariables.clear();
1199 if ( !mSettings.
value( QStringLiteral(
"projects/anonymize_new_projects" ),
false,
QgsSettings::Core ).toBool() )
1218 mEmbeddedLayers.clear();
1219 mRelationManager->
clear();
1220 mAnnotationManager->clear();
1221 mLayoutManager->clear();
1222 m3DViewsManager->clear();
1223 mBookmarkManager->
clear();
1224 mSensorManager->
clear();
1225 mViewSettings->
reset();
1226 mTimeSettings->
reset();
1227 mElevationProperties->
reset();
1228 mDisplaySettings->
reset();
1229 mGpsSettings->
reset();
1230 mSnappingConfig.
reset();
1238 mLabelingEngineSettings->clear();
1242 releaseHandlesToProjectArchive();
1248 mStyleSettings->
reset();
1252 if ( !mIsBeingDeleted )
1260 writeEntry( QStringLiteral(
"PositionPrecision" ), QStringLiteral(
"/Automatic" ),
true );
1261 writeEntry( QStringLiteral(
"PositionPrecision" ), QStringLiteral(
"/DecimalPlaces" ), 2 );
1263 const bool defaultRelativePaths = mSettings.
value( QStringLiteral(
"/qgis/defaultProjectPathsRelative" ),
true ).toBool();
1266 int red = mSettings.
value( QStringLiteral(
"qgis/default_canvas_color_red" ), 255 ).toInt();
1267 int green = mSettings.
value( QStringLiteral(
"qgis/default_canvas_color_green" ), 255 ).toInt();
1268 int blue = mSettings.
value( QStringLiteral(
"qgis/default_canvas_color_blue" ), 255 ).toInt();
1271 red = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_red" ), 255 ).toInt();
1272 green = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_green" ), 255 ).toInt();
1273 blue = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_blue" ), 0 ).toInt();
1274 const int alpha = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_alpha" ), 255 ).toInt();
1280 mRootGroup->
clear();
1281 if ( mMainAnnotationLayer )
1282 mMainAnnotationLayer->
reset();
1284 snapSingleBlocker.release();
1286 if ( !mBlockSnappingUpdates )
1291 if ( !mBlockChangeSignalsDuringClear )
1303 topQgsPropertyKey.
dump();
1336 const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral(
"properties" ) );
1338 if ( propertiesElem.isNull() )
1343 const QDomNodeList scopes = propertiesElem.childNodes();
1345 if ( propertiesElem.firstChild().isNull() )
1347 QgsDebugError( QStringLiteral(
"empty ``properties'' XML tag ... bailing" ) );
1351 if ( ! project_properties.
readXml( propertiesElem ) )
1353 QgsDebugError( QStringLiteral(
"Project_properties.readXml() failed" ) );
1367 const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral(
"dataDefinedServerProperties" ) );
1368 if ( !ddElem.isNull() )
1370 if ( !ddServerProperties.
readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1372 QgsDebugError( QStringLiteral(
"dataDefinedServerProperties.readXml() failed" ) );
1375 return ddServerProperties;
1382static void _getTitle(
const QDomDocument &doc, QString &title )
1384 const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral(
"title" ) );
1388 if ( titleNode.isNull() )
1394 if ( !titleNode.hasChildNodes() )
1400 const QDomNode titleTextNode = titleNode.firstChild();
1402 if ( !titleTextNode.isText() )
1408 const QDomText titleText = titleTextNode.toText();
1410 title = titleText.data();
1414static void readProjectFileMetadata(
const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1416 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral(
"qgis" ) );
1420 QgsDebugError( QStringLiteral(
"unable to find qgis element" ) );
1424 const QDomNode qgisNode = nl.item( 0 );
1426 const QDomElement qgisElement = qgisNode.toElement();
1427 lastUser = qgisElement.attribute( QStringLiteral(
"saveUser" ), QString() );
1428 lastUserFull = qgisElement.attribute( QStringLiteral(
"saveUserFull" ), QString() );
1429 lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral(
"saveDateTime" ), QString() ), Qt::ISODate );
1434 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral(
"qgis" ) );
1438 QgsDebugError( QStringLiteral(
" unable to find qgis element in project file" ) );
1442 const QDomNode qgisNode = nl.item( 0 );
1444 const QDomElement qgisElement = qgisNode.toElement();
1445 QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral(
"version" ) ) );
1446 return projectVersion;
1453 return mSnappingConfig;
1472 if ( mAvoidIntersectionsMode == mode )
1475 mAvoidIntersectionsMode = mode;
1509void QgsProject::preloadProviders(
const QVector<QDomNode> ¶llelLayerNodes,
1511 QMap<QString, QgsDataProvider *> &loadedProviders,
1513 int totalProviderCount )
1518 QMap<QString, LayerToLoad> layersToLoad;
1520 for (
const QDomNode &node : parallelLayerNodes )
1524 const QDomElement layerElement = node.toElement();
1526 layerToLoad.
layerId = layerElement.namedItem( QStringLiteral(
"id" ) ).toElement().text();
1527 layerToLoad.
provider = layerElement.namedItem( QStringLiteral(
"provider" ) ).toElement().text();
1528 layerToLoad.
dataSource = layerElement.namedItem( QStringLiteral(
"datasource" ) ).toElement().text();
1539 layersToLoad.insert( layerToLoad.
layerId, layerToLoad );
1542 while ( !layersToLoad.isEmpty() )
1544 const QList<LayerToLoad> layersToAttemptInParallel = layersToLoad.values();
1545 QString layerToAttemptInMainThread;
1547 QHash<QString, QgsRunnableProviderCreator *> runnables;
1548 QThreadPool threadPool;
1551 for (
const LayerToLoad &lay : layersToAttemptInParallel )
1554 runnables.insert( lay.layerId, run );
1560 layersToLoad.remove( layId );
1563 Q_ASSERT( finishedRun );
1565 std::unique_ptr<QgsDataProvider> provider( finishedRun->
dataProvider() );
1566 Q_ASSERT( provider && provider->isValid() );
1568 loadedProviders.insert( layId, provider.release() );
1573 if ( layerToAttemptInMainThread.isEmpty() )
1574 layerToAttemptInMainThread = layId;
1578 if ( i == parallelLayerNodes.count() || !isValid )
1581 threadPool.start( run );
1585 threadPool.waitForDone();
1587 qDeleteAll( runnables );
1590 auto it = layersToLoad.find( layerToAttemptInMainThread );
1591 if ( it != layersToLoad.end() )
1593 std::unique_ptr<QgsDataProvider> provider;
1603 if ( provider && provider->isValid() )
1608 layersToLoad.erase( it );
1611 loadedProviders.insert( layerId, provider.release() );
1619void QgsProject::releaseHandlesToProjectArchive()
1624bool QgsProject::rebuildCrs3D( QString *error )
1631 else if ( !mVerticalCrs.
isValid() )
1637 switch ( mCrs.
type() )
1678bool QgsProject::_getMapLayers(
const QDomDocument &doc, QList<QDomNode> &brokenNodes,
Qgis::ProjectReadFlags flags )
1685 QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral(
"projectlayers" ) ).firstChildElement( QStringLiteral(
"maplayer" ) );
1689 if ( layerElement.isNull() )
1699 bool returnStatus =
true;
1702 while ( ! layerElement.isNull() )
1705 layerElement = layerElement.nextSiblingElement( QStringLiteral(
"maplayer" ) );
1711 if ( depSorter.hasCycle() )
1715 if ( depSorter.hasMissingDependency() )
1716 returnStatus =
false;
1720 const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1721 const int totalLayerCount = sortedLayerNodes.count();
1723 QVector<QDomNode> parallelLoading;
1724 QMap<QString, QgsDataProvider *> loadedProviders;
1729 profile.switchTask( tr(
"Load providers in parallel" ) );
1730 for (
const QDomNode &node : sortedLayerNodes )
1732 const QDomElement element = node.toElement();
1733 if ( element.attribute( QStringLiteral(
"embedded" ) ) != QLatin1String(
"1" ) )
1735 const QString layerId = node.namedItem( QStringLiteral(
"id" ) ).toElement().text();
1736 if ( !depSorter.isLayerDependent( layerId ) )
1738 const QDomNode mnl = element.namedItem( QStringLiteral(
"provider" ) );
1739 const QDomElement mne = mnl.toElement();
1740 const QString provider = mne.text();
1744 parallelLoading.append( node );
1753 if ( !parallelLoading.isEmpty() )
1754 preloadProviders( parallelLoading, context, loadedProviders, projectFlagsToLayerReadFlags(
flags, mFlags ), sortedLayerNodes.count() );
1757 int i = loadedProviders.count();
1758 for (
const QDomNode &node : std::as_const( sortedLayerNodes ) )
1760 const QDomElement element = node.toElement();
1761 const QString name =
translate( QStringLiteral(
"project:layers:%1" ).arg( node.namedItem( QStringLiteral(
"id" ) ).toElement().text() ), node.namedItem( QStringLiteral(
"layername" ) ).toElement().text() );
1762 if ( !name.isNull() )
1763 emit
loadingLayer( tr(
"Loading layer %1" ).arg( name ) );
1765 profile.switchTask( name );
1766 if ( element.attribute( QStringLiteral(
"embedded" ) ) == QLatin1String(
"1" ) )
1768 createEmbeddedLayer( element.attribute( QStringLiteral(
"id" ) ),
readPath( element.attribute( QStringLiteral(
"project" ) ) ), brokenNodes,
true,
flags );
1776 QString layerId = element.namedItem( QStringLiteral(
"id" ) ).toElement().text();
1778 if ( !addLayer( element, brokenNodes, context,
flags, loadedProviders.take( layerId ) ) )
1780 returnStatus =
false;
1783 if ( !messages.isEmpty() )
1792 return returnStatus;
1795bool QgsProject::addLayer(
const QDomElement &layerElem,
1796 QList<QDomNode> &brokenNodes,
1803 const QString type = layerElem.attribute( QStringLiteral(
"type" ) );
1805 std::unique_ptr<QgsMapLayer>
mapLayer;
1813 QgsDebugError( QStringLiteral(
"Unknown layer type \"%1\"" ).arg( type ) );
1817 switch ( layerType )
1820 mapLayer = std::make_unique<QgsVectorLayer>();
1824 mapLayer = std::make_unique<QgsRasterLayer>();
1828 mapLayer = std::make_unique<QgsMeshLayer>();
1832 mapLayer = std::make_unique<QgsVectorTileLayer>();
1836 mapLayer = std::make_unique<QgsPointCloudLayer>();
1840 mapLayer = std::make_unique<QgsTiledSceneLayer>();
1845 const QString
typeName = layerElem.attribute( QStringLiteral(
"name" ) );
1853 mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1860 mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1867 QgsDebugError( QStringLiteral(
"Unable to create layer" ) );
1875 const QString layerId { layerElem.namedItem( QStringLiteral(
"id" ) ).toElement().text() };
1876 Q_ASSERT( ! layerId.isEmpty() );
1882 profile.switchTask( tr(
"Load layer source" ) );
1889 if ( vl->dataProvider() )
1896 profile.switchTask( tr(
"Add layer to project" ) );
1897 QList<QgsMapLayer *> newLayers;
1909 vLayer->joinBuffer()->resolveReferences(
this );
1918 brokenNodes.push_back( layerElem );
1921 const bool wasEditable = layerElem.attribute( QStringLiteral(
"editable" ), QStringLiteral(
"0" ) ).toInt();
1933 if ( ! layerWasStored )
1938 return layerIsValid;
1945 mFile.setFileName( filename );
1946 mCachedHomePath.clear();
1947 mProjectScope.reset();
1956 const QString filename = mFile.fileName();
1961 QTemporaryFile inDevice;
1962 if ( !inDevice.open() )
1964 setError( tr(
"Unable to open %1" ).arg( inDevice.fileName() ) );
1970 if ( !storage->readProject( filename, &inDevice, context ) )
1972 QString err = tr(
"Unable to open %1" ).arg( filename );
1973 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.
takeMessages();
1974 if ( !messages.isEmpty() )
1975 err += QStringLiteral(
"\n\n" ) + messages.last().message();
1979 returnValue = unzip( inDevice.fileName(),
flags );
1985 returnValue = unzip( mFile.fileName(),
flags );
1990 const QFileInfo finfo( mFile.fileName() );
1991 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral(
"%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1992 if ( QFile( attachmentsZip ).exists() )
1994 std::unique_ptr<QgsArchive> archive(
new QgsArchive() );
1995 if ( archive->unzip( attachmentsZip ) )
1997 releaseHandlesToProjectArchive();
1998 mArchive = std::move( archive );
2001 returnValue = readProjectFile( mFile.fileName(),
flags );
2007 mFile.setFileName( filename );
2008 mCachedHomePath.clear();
2009 mProjectScope.reset();
2014 mTranslator.reset(
nullptr );
2026 ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
2028 QFile projectFile( filename );
2036 if ( QFile( QStringLiteral(
"%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
2038 mTranslator.reset(
new QTranslator() );
2039 ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
2042 profile.switchTask( tr(
"Reading project file" ) );
2043 std::unique_ptr<QDomDocument> doc(
new QDomDocument( QStringLiteral(
"qgis" ) ) );
2045 if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
2047 projectFile.close();
2049 setError( tr(
"Unable to open %1" ).arg( projectFile.fileName() ) );
2054 QTextStream textStream( &projectFile );
2055#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2056 textStream.setCodec(
"UTF-8" );
2058 QString projectString = textStream.readAll();
2059 projectFile.close();
2061 for (
int i = 0; i < 32; i++ )
2063 if ( i == 9 || i == 10 || i == 13 )
2067 projectString.replace( QChar( i ), QStringLiteral(
"%1%2%1" ).arg(
FONTMARKER_CHR_FIX, QString::number( i ) ) );
2073 if ( !doc->setContent( projectString, &errorMsg, &line, &column ) )
2075 const QString errorString = tr(
"Project file read error in file %1: %2 at line %3 column %4" )
2076 .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
2078 setError( errorString );
2083 projectFile.close();
2091 profile.switchTask( tr(
"Updating project file" ) );
2092 if ( thisVersion > fileVersion )
2094 const bool isOlderMajorVersion = fileVersion.
majorVersion() < thisVersion.majorVersion();
2096 if ( isOlderMajorVersion )
2099 "version of qgis (saved in " + fileVersion.
text() +
2101 "). Problems may occur." );
2112 projectFile.updateRevision( thisVersion );
2114 else if ( fileVersion > thisVersion )
2117 "version of qgis (saved in " + fileVersion.
text() +
2119 "). Problems may occur." );
2125 profile.switchTask( tr(
"Creating auxiliary storage" ) );
2126 const QString
fileName = mFile.fileName();
2133 std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
2134 std::unique_ptr<QgsArchive> archive = std::move( mArchive );
2138 mBlockChangeSignalsDuringClear =
true;
2140 mBlockChangeSignalsDuringClear =
false;
2145 releaseHandlesToProjectArchive();
2147 mAuxiliaryStorage = std::move( aStorage );
2148 mArchive = std::move( archive );
2151 mCachedHomePath.clear();
2152 mProjectScope.reset();
2153 mSaveVersion = fileVersion;
2156 profile.switchTask( tr(
"Reading properties" ) );
2165 dump_( mProperties );
2170 _getTitle( *doc, oldTitle );
2172 readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
2174 const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral(
"homePath" ) );
2175 if ( homePathNl.count() > 0 )
2177 const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
2178 const QString
homePath = homePathElement.attribute( QStringLiteral(
"path" ) );
2188 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorGreenPart" ), 255 ),
2189 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorBluePart" ), 255 ) );
2192 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorGreenPart" ), 255 ),
2193 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorBluePart" ), 255 ),
2194 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorAlphaPart" ), 255 ) );
2198 const QString distanceUnitString =
readEntry( QStringLiteral(
"Measurement" ), QStringLiteral(
"/DistanceUnits" ), QString() );
2199 if ( !distanceUnitString.isEmpty() )
2202 const QString areaUnitString =
readEntry( QStringLiteral(
"Measurement" ), QStringLiteral(
"/AreaUnits" ), QString() );
2203 if ( !areaUnitString.isEmpty() )
2212 if (
readNumEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectionsEnabled" ), 0 ) )
2215 const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral(
"projectCrs" ) );
2216 if ( !srsNode.isNull() )
2218 projectCrs.
readXml( srsNode );
2223 const QString projCrsString =
readEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectCRSProj4String" ) );
2224 const long currentCRS =
readNumEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectCRSID" ), -1 );
2225 const QString authid =
readEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectCrs" ) );
2228 const bool isUserAuthId = authid.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive );
2229 if ( !authid.isEmpty() && !isUserAuthId )
2233 if ( !projectCrs.
isValid() && currentCRS >= 0 )
2239 if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.
isValid() || projectCrs.
toProj() != projCrsString ) )
2256 const QDomNode verticalCrsNode = doc->documentElement().namedItem( QStringLiteral(
"verticalCrs" ) );
2257 if ( !verticalCrsNode.isNull() )
2265 QStringList datumErrors;
2266 if ( !mTransformContext.
readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
2273 const QDomNode elevationShadingNode = doc->documentElement().namedItem( QStringLiteral(
"elevation-shading-renderer" ) );
2274 if ( !elevationShadingNode.isNull() )
2276 mElevationShadingRenderer.
readXml( elevationShadingNode.toElement(), context );
2283 const QStringList variableNames =
readListEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableNames" ) );
2284 const QStringList variableValues =
readListEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableValues" ) );
2286 mCustomVariables.clear();
2287 if ( variableNames.length() == variableValues.length() )
2289 for (
int i = 0; i < variableNames.length(); ++i )
2291 mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
2296 QgsMessageLog::logMessage( tr(
"Project Variables Invalid" ), tr(
"The project contains invalid variable settings." ) );
2304 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral(
"projectMetadata" ) );
2306 if ( !element.isNull() )
2315 if ( mMetadata.
title().isEmpty() && !oldTitle.isEmpty() )
2323 element = doc->documentElement().firstChildElement( QStringLiteral(
"transaction" ) );
2324 if ( !element.isNull() )
2331 element = doc->documentElement().firstChildElement( QStringLiteral(
"autotransaction" ) );
2332 if ( ! element.isNull() )
2334 mTransactionMode =
static_cast<Qgis::TransactionMode>( element.attribute( QStringLiteral(
"active" ), QStringLiteral(
"0" ) ).toInt() );
2339 profile.switchTask( tr(
"Loading layer tree" ) );
2342 QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral(
"layer-tree-group" ) );
2343 if ( !layerTreeElem.isNull() )
2355 mLayerTreeRegistryBridge->
setEnabled(
false );
2358 profile.switchTask( tr(
"Reading map layers" ) );
2360 loadProjectFlags( doc.get() );
2362 QList<QDomNode> brokenNodes;
2363 const bool clean = _getMapLayers( *doc, brokenNodes,
flags );
2368 QgsDebugError( QStringLiteral(
"Unable to get map layers from project file." ) );
2370 if ( !brokenNodes.isEmpty() )
2372 QgsDebugError(
"there are " + QString::number( brokenNodes.size() ) +
" broken layers" );
2380 mMainAnnotationLayer->
readLayerXml( doc->documentElement().firstChildElement( QStringLiteral(
"main-annotation-layer" ) ), context );
2384 profile.switchTask( tr(
"Loading embedded layers" ) );
2385 loadEmbeddedNodes( mRootGroup,
flags );
2389 profile.switchTask( tr(
"Resolving layer references" ) );
2390 QMap<QString, QgsMapLayer *>
layers = mLayerStore->mapLayers();
2391 for ( QMap<QString, QgsMapLayer *>::iterator it =
layers.begin(); it !=
layers.end(); ++it )
2393 it.value()->resolveReferences(
this );
2397 mLayerTreeRegistryBridge->
setEnabled(
true );
2400 profile.switchTask( tr(
"Resolving references" ) );
2411 if ( !layerTreeElem.isNull() )
2417 const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral(
"layer-tree-canvas" ) );
2418 if ( !layerTreeCanvasElem.isNull( ) )
2426 const QStringList requiredLayerIds =
readListEntry( QStringLiteral(
"RequiredLayers" ), QStringLiteral(
"Layers" ) );
2427 for (
const QString &layerId : requiredLayerIds )
2434 const QStringList disabledLayerIds =
readListEntry( QStringLiteral(
"Identify" ), QStringLiteral(
"/disabledLayers" ) );
2435 for (
const QString &layerId : disabledLayerIds )
2448 QString styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Marker" ) );
2449 if ( !styleName.isEmpty() )
2454 styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Line" ) );
2455 if ( !styleName.isEmpty() )
2460 styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Fill" ) );
2461 if ( !styleName.isEmpty() )
2466 styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/ColorRamp" ) );
2467 if ( !styleName.isEmpty() )
2477 double opacity = 1.0;
2480 double alpha =
readDoubleEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/AlphaInt" ), 255, &ok );
2482 opacity = alpha / 255.0;
2483 double newOpacity =
readDoubleEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Opacity" ), 1.0, &ok );
2485 opacity = newOpacity;
2489 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Marker" ) );
2490 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Line" ) );
2491 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Fill" ) );
2492 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/ColorRamp" ) );
2493 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/RandomColors" ) );
2494 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/AlphaInt" ) );
2495 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Opacity" ) );
2503 profile.switchTask( tr(
"Storing original layer properties" ) );
2509 profile.switchTask( tr(
"Loading map themes" ) );
2512 mMapThemeCollection->readXml( *doc );
2514 profile.switchTask( tr(
"Loading label settings" ) );
2515 mLabelingEngineSettings->readSettingsFromProject(
this );
2517 const QDomElement labelEngineSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"labelEngineSettings" ) );
2518 mLabelingEngineSettings->readXml( labelEngineSettingsElement, context );
2520 mLabelingEngineSettings->resolveReferences(
this );
2524 profile.switchTask( tr(
"Loading annotations" ) );
2527 mAnnotationManager->readXml( doc->documentElement(), context );
2531 mAnnotationManager->readXmlAndUpgradeToAnnotationLayerItems( doc->documentElement(), context, mMainAnnotationLayer, mTransformContext );
2535 profile.switchTask( tr(
"Loading layouts" ) );
2536 mLayoutManager->readXml( doc->documentElement(), *doc );
2541 profile.switchTask( tr(
"Loading 3D Views" ) );
2542 m3DViewsManager->readXml( doc->documentElement(), *doc );
2545 profile.switchTask( tr(
"Loading bookmarks" ) );
2546 mBookmarkManager->
readXml( doc->documentElement(), *doc );
2548 profile.switchTask( tr(
"Loading sensors" ) );
2549 mSensorManager->
readXml( doc->documentElement(), *doc );
2552 QMap<QString, QgsMapLayer *> existingMaps =
mapLayers();
2553 for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
2555 it.value()->setDependencies( it.value()->dependencies() );
2558 profile.switchTask( tr(
"Loading snapping settings" ) );
2562 profile.switchTask( tr(
"Loading view settings" ) );
2565 const QStringList scales =
readListEntry( QStringLiteral(
"Scales" ), QStringLiteral(
"/ScalesList" ) );
2566 QVector<double> res;
2567 for (
const QString &scale : scales )
2569 const QStringList parts = scale.split(
':' );
2570 if ( parts.size() != 2 )
2574 const double denominator = QLocale().toDouble( parts[1], &ok );
2581 const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectViewSettings" ) );
2582 if ( !viewSettingsElement.isNull() )
2583 mViewSettings->
readXml( viewSettingsElement, context );
2586 profile.switchTask( tr(
"Loading style properties" ) );
2587 const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectStyleSettings" ) );
2588 if ( !styleSettingsElement.isNull() )
2591 mStyleSettings->
readXml( styleSettingsElement, context,
flags );
2595 profile.switchTask( tr(
"Loading temporal settings" ) );
2596 const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectTimeSettings" ) );
2597 if ( !timeSettingsElement.isNull() )
2598 mTimeSettings->
readXml( timeSettingsElement, context );
2601 profile.switchTask( tr(
"Loading elevation properties" ) );
2602 const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( QStringLiteral(
"ElevationProperties" ) );
2603 if ( !elevationPropertiesElement.isNull() )
2604 mElevationProperties->
readXml( elevationPropertiesElement, context );
2607 profile.switchTask( tr(
"Loading display settings" ) );
2609 const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectDisplaySettings" ) );
2610 if ( !displaySettingsElement.isNull() )
2611 mDisplaySettings->
readXml( displaySettingsElement, context );
2614 profile.switchTask( tr(
"Loading GPS settings" ) );
2616 const QDomElement gpsSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectGpsSettings" ) );
2617 if ( !gpsSettingsElement.isNull() )
2618 mGpsSettings->
readXml( gpsSettingsElement, context );
2622 profile.switchTask( tr(
"Updating variables" ) );
2624 profile.switchTask( tr(
"Updating CRS" ) );
2628 if ( mCrs3D != oldCrs3D )
2633 profile.switchTask( tr(
"Reading external settings" ) );
2637 profile.switchTask( tr(
"Updating interface" ) );
2639 snapSignalBlock.release();
2640 if ( !mBlockSnappingUpdates )
2651 QgsDebugMsgLevel( QStringLiteral(
"Project save user: %1" ).arg( mSaveUser ), 2 );
2652 QgsDebugMsgLevel( QStringLiteral(
"Project save user: %1" ).arg( mSaveUserFull ), 2 );
2661 const QString newFileName( QStringLiteral(
"%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
2676 const QMap<QString, QgsMapLayer *> loadedLayers =
mapLayers();
2677 for (
auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2679 if ( it.value()->isValid() && it.value()->customProperty( QStringLiteral(
"_layer_was_editable" ) ).toBool() )
2681 if (
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2683 it.value()->removeCustomProperty( QStringLiteral(
"_layer_was_editable" ) );
2695 const auto constChildren = group->
children();
2701 if ( childGroup->
customProperty( QStringLiteral(
"embedded" ) ).toInt() )
2704 const QString projectPath =
readPath( childGroup->
customProperty( QStringLiteral(
"embedded_project" ) ).toString() );
2705 childGroup->
setCustomProperty( QStringLiteral(
"embedded_project" ), projectPath );
2709 QList<QgsLayerTreeNode *> clonedChildren;
2710 const QList<QgsLayerTreeNode *> constChildren = newGroup->
children();
2711 clonedChildren.reserve( constChildren.size() );
2713 clonedChildren << newGroupChild->clone();
2721 loadEmbeddedNodes( childGroup,
flags );
2726 if ( child->customProperty( QStringLiteral(
"embedded" ) ).toInt() )
2728 QList<QDomNode> brokenNodes;
2731 valid = valid &&
false;
2746 return mCustomVariables;
2753 if ( variables == mCustomVariables )
2757 QStringList variableNames;
2758 QStringList variableValues;
2760 QVariantMap::const_iterator it = variables.constBegin();
2761 for ( ; it != variables.constEnd(); ++it )
2763 variableNames << it.key();
2764 variableValues << it.value().toString();
2767 writeEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableNames" ), variableNames );
2768 writeEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableValues" ), variableValues );
2770 mCustomVariables = variables;
2771 mProjectScope.reset();
2780 *mLabelingEngineSettings = settings;
2788 return *mLabelingEngineSettings;
2795 mProjectScope.reset();
2796 return mLayerStore.get();
2803 return mLayerStore.get();
2810 QList<QgsVectorLayer *>
layers;
2811 const QStringList layerIds =
readListEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/AvoidIntersectionsList" ), QStringList() );
2812 const auto constLayerIds = layerIds;
2813 for (
const QString &layerId : constLayerIds )
2826 list.reserve(
layers.size() );
2831 list << layer->id();
2834 writeEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/AvoidIntersectionsList" ), list );
2856 if ( mProjectScope )
2858 std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2865 projectScope->addFunction( QStringLiteral(
"sensor_data" ),
new GetSensorData(
sensorManager()->sensorsData() ) );
2867 return projectScope.release();
2870 mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr(
"Project" ) );
2874 QVariantMap::const_iterator it = vars.constBegin();
2876 for ( ; it != vars.constEnd(); ++it )
2878 mProjectScope->setVariable( it.key(), it.value(),
true );
2882 if ( projectPath.isEmpty() )
2883 projectPath = mOriginalPath;
2884 const QString projectFolder = QFileInfo( projectPath ).path();
2885 const QString projectFilename = QFileInfo( projectPath ).fileName();
2886 const QString projectBasename =
baseName();
2923 QVariantMap keywords;
2925 for (
auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2927 keywords.insert( it.key(), it.value() );
2932 QVariantList layersIds;
2934 const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2935 layersIds.reserve( layersInProject.count() );
2936 layers.reserve( layersInProject.count() );
2937 for (
auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2939 layersIds << it.value()->id();
2945 mProjectScope->addFunction( QStringLiteral(
"project_color" ),
new GetNamedProjectColor(
this ) );
2946 mProjectScope->addFunction( QStringLiteral(
"project_color_object" ),
new GetNamedProjectColorObject(
this ) );
2951void QgsProject::onMapLayersAdded(
const QList<QgsMapLayer *> &layers )
2955 const QMap<QString, QgsMapLayer *> existingMaps =
mapLayers();
2957 const auto constLayers =
layers;
2960 if ( ! layer->isValid() )
2963 if (
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
2966 if ( vlayer->dataProvider() )
2974 for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2976 const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2977 if ( deps.contains( layer->id() ) )
2980 it.value()->setDependencies( deps );
2985 updateTransactionGroups();
2991void QgsProject::onMapLayersRemoved(
const QList<QgsMapLayer *> &layers )
3000 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
3008void QgsProject::cleanTransactionGroups(
bool force )
3012 bool changed =
false;
3013 for ( QMap< QPair< QString, QString>,
QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
3015 if ( tg.value()->isEmpty() || force )
3018 tg = mTransactionGroups.erase( tg );
3030void QgsProject::updateTransactionGroups()
3034 mEditBufferGroup.
clear();
3036 switch ( mTransactionMode )
3040 cleanTransactionGroups(
true );
3045 cleanTransactionGroups(
true );
3048 cleanTransactionGroups(
false );
3052 bool tgChanged =
false;
3053 const auto constLayers =
mapLayers().values();
3056 if ( ! layer->isValid() )
3059 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
3063 switch ( mTransactionMode )
3080 mTransactionGroups.insert( qMakePair( key, connString ), tg );
3090 mEditBufferGroup.
addLayer( vlayer );
3106 context.setProjectTranslator(
this );
3108 QList<QDomNode> brokenNodes;
3109 if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
3113 const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
3117 layer->resolveReferences(
this );
3119 if ( layer->isValid() && layer->customProperty( QStringLiteral(
"_layer_was_editable" ) ).toBool() )
3121 layer->startEditing();
3122 layer->removeCustomProperty( QStringLiteral(
"_layer_was_editable" ) );
3134 mFile.setFileName( filename );
3135 mCachedHomePath.clear();
3143 mProjectScope.reset();
3149 const QString storageFilePath { storage->filePath( mFile.fileName() ) };
3150 if ( storageFilePath.isEmpty() )
3156 const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
3157 const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
3159 if ( !zip( tmpZipFilename ) )
3162 QFile tmpZipFile( tmpZipFilename );
3163 if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
3165 setError( tr(
"Unable to read file %1" ).arg( tmpZipFilename ) );
3170 if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
3172 QString err = tr(
"Unable to save project to storage %1" ).arg( mFile.fileName() );
3173 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.
takeMessages();
3174 if ( !messages.isEmpty() )
3175 err += QStringLiteral(
"\n\n" ) + messages.last().message();
3181 QFile::remove( tmpZipFilename );
3188 return zip( mFile.fileName() );
3194 const bool asOk = saveAuxiliaryStorage();
3195 const bool writeOk = writeProjectFile( mFile.fileName() );
3196 bool attachmentsOk =
true;
3197 if ( !mArchive->files().isEmpty() )
3199 const QFileInfo finfo( mFile.fileName() );
3200 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral(
"%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
3201 attachmentsOk = mArchive->zip( attachmentsZip );
3205 if ( ( !asOk || !attachmentsOk ) && writeOk )
3207 QStringList errorMessage;
3210 const QString err = mAuxiliaryStorage->errorString();
3211 errorMessage.append( tr(
"Unable to save auxiliary storage ('%1')" ).arg( err ) );
3213 if ( !attachmentsOk )
3215 errorMessage.append( tr(
"Unable to save attachments archive" ) );
3217 setError( errorMessage.join(
'\n' ) );
3220 return asOk && writeOk && attachmentsOk;
3224bool QgsProject::writeProjectFile(
const QString &filename )
3228 QFile projectFile( filename );
3234 const QFileInfo myFileInfo( projectFile );
3235 if ( myFileInfo.exists() && !myFileInfo.isWritable() )
3237 setError( tr(
"%1 is not writable. Please adjust permissions (if possible) and try again." )
3238 .arg( projectFile.fileName() ) );
3246 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
3248 const QDomDocumentType documentType =
3249 QDomImplementation().createDocumentType( QStringLiteral(
"qgis" ), QStringLiteral(
"http://mrcc.com/qgis.dtd" ),
3250 QStringLiteral(
"SYSTEM" ) );
3251 std::unique_ptr<QDomDocument> doc(
new QDomDocument( documentType ) );
3253 QDomElement qgisNode = doc->createElement( QStringLiteral(
"qgis" ) );
3254 qgisNode.setAttribute( QStringLiteral(
"projectname" ),
title() );
3255 qgisNode.setAttribute( QStringLiteral(
"version" ),
Qgis::version() );
3257 if ( !mSettings.
value( QStringLiteral(
"projects/anonymize_saved_projects" ),
false,
QgsSettings::Core ).toBool() )
3261 qgisNode.setAttribute( QStringLiteral(
"saveUser" ), newSaveUser );
3262 qgisNode.setAttribute( QStringLiteral(
"saveUserFull" ), newSaveUserFull );
3263 mSaveUser = newSaveUser;
3264 mSaveUserFull = newSaveUserFull;
3265 mSaveDateTime = QDateTime::currentDateTime();
3266 qgisNode.setAttribute( QStringLiteral(
"saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
3271 mSaveUserFull.clear();
3272 mSaveDateTime = QDateTime();
3274 doc->appendChild( qgisNode );
3277 QDomElement homePathNode = doc->createElement( QStringLiteral(
"homePath" ) );
3278 homePathNode.setAttribute( QStringLiteral(
"path" ), mHomePath );
3279 qgisNode.appendChild( homePathNode );
3282 QDomElement titleNode = doc->createElement( QStringLiteral(
"title" ) );
3283 qgisNode.appendChild( titleNode );
3285 QDomElement transactionNode = doc->createElement( QStringLiteral(
"transaction" ) );
3286 transactionNode.setAttribute( QStringLiteral(
"mode" ),
qgsEnumValueToKey( mTransactionMode ) );
3287 qgisNode.appendChild( transactionNode );
3289 QDomElement flagsNode = doc->createElement( QStringLiteral(
"projectFlags" ) );
3291 qgisNode.appendChild( flagsNode );
3293 const QDomText titleText = doc->createTextNode(
title() );
3294 titleNode.appendChild( titleText );
3298 QDomElement srsNode = doc->createElement( QStringLiteral(
"projectCrs" ) );
3300 qgisNode.appendChild( srsNode );
3303 QDomElement verticalSrsNode = doc->createElement( QStringLiteral(
"verticalCrs" ) );
3304 mVerticalCrs.
writeXml( verticalSrsNode, *doc );
3305 qgisNode.appendChild( verticalSrsNode );
3308 QDomElement elevationShadingNode = doc->createElement( QStringLiteral(
"elevation-shading-renderer" ) );
3309 mElevationShadingRenderer.
writeXml( elevationShadingNode, context );
3310 qgisNode.appendChild( elevationShadingNode );
3317 clonedRoot->
writeXml( qgisNode, context );
3321 writeEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/AvoidIntersectionsMode" ),
static_cast<int>( mAvoidIntersectionsMode ) );
3329 QDomElement annotationLayerNode = doc->createElement( QStringLiteral(
"main-annotation-layer" ) );
3330 mMainAnnotationLayer->
writeLayerXml( annotationLayerNode, *doc, context );
3331 qgisNode.appendChild( annotationLayerNode );
3335 QDomElement projectLayersNode = doc->createElement( QStringLiteral(
"projectlayers" ) );
3337 QMap<QString, QgsMapLayer *>::ConstIterator li =
layers.constBegin();
3338 while ( li !=
layers.end() )
3344 const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->
id() );
3345 if ( emIt == mEmbeddedLayers.constEnd() )
3347 QDomElement maplayerElem;
3353 maplayerElem = doc->createElement( QStringLiteral(
"maplayer" ) );
3357 maplayerElem.setAttribute( QStringLiteral(
"editable" ), QStringLiteral(
"1" ) );
3361 QDomDocument document;
3364 maplayerElem = document.firstChildElement();
3368 QgsDebugError( QStringLiteral(
"Could not restore layer properties for layer %1" ).arg( ml->
id() ) );
3374 projectLayersNode.appendChild( maplayerElem );
3380 if ( emIt.value().second )
3382 QDomElement mapLayerElem = doc->createElement( QStringLiteral(
"maplayer" ) );
3383 mapLayerElem.setAttribute( QStringLiteral(
"embedded" ), 1 );
3384 mapLayerElem.setAttribute( QStringLiteral(
"project" ),
writePath( emIt.value().first ) );
3385 mapLayerElem.setAttribute( QStringLiteral(
"id" ), ml->
id() );
3386 projectLayersNode.appendChild( mapLayerElem );
3393 qgisNode.appendChild( projectLayersNode );
3395 QDomElement layerOrderNode = doc->createElement( QStringLiteral(
"layerorder" ) );
3397 for (
QgsMapLayer *layer : constCustomLayerOrder )
3399 QDomElement mapLayerElem = doc->createElement( QStringLiteral(
"layer" ) );
3400 mapLayerElem.setAttribute( QStringLiteral(
"id" ), layer->id() );
3401 layerOrderNode.appendChild( mapLayerElem );
3403 qgisNode.appendChild( layerOrderNode );
3405 mLabelingEngineSettings->writeSettingsToProject(
this );
3407 QDomElement labelEngineSettingsElement = doc->createElement( QStringLiteral(
"labelEngineSettings" ) );
3408 mLabelingEngineSettings->writeXml( *doc, labelEngineSettingsElement, context );
3409 qgisNode.appendChild( labelEngineSettingsElement );
3412 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorRedPart" ), mBackgroundColor.red() );
3413 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorGreenPart" ), mBackgroundColor.green() );
3414 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorBluePart" ), mBackgroundColor.blue() );
3416 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorRedPart" ), mSelectionColor.red() );
3417 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorGreenPart" ), mSelectionColor.green() );
3418 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorBluePart" ), mSelectionColor.blue() );
3419 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
3426 dump_( mProperties );
3429 QgsDebugMsgLevel( QStringLiteral(
"there are %1 property scopes" ).arg(
static_cast<int>( mProperties.
count() ) ), 2 );
3434 mProperties.
writeXml( QStringLiteral(
"properties" ), qgisNode, *doc );
3437 QDomElement ddElem = doc->createElement( QStringLiteral(
"dataDefinedServerProperties" ) );
3438 mDataDefinedServerProperties.
writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
3439 qgisNode.appendChild( ddElem );
3441 mMapThemeCollection->writeXml( *doc );
3443 mTransformContext.
writeXml( qgisNode, context );
3445 QDomElement metadataElem = doc->createElement( QStringLiteral(
"projectMetadata" ) );
3447 qgisNode.appendChild( metadataElem );
3450 const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
3451 qgisNode.appendChild( annotationsElem );
3455 const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
3456 qgisNode.appendChild( layoutElem );
3460 const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
3461 qgisNode.appendChild( views3DElem );
3465 const QDomElement bookmarkElem = mBookmarkManager->
writeXml( *doc );
3466 qgisNode.appendChild( bookmarkElem );
3470 const QDomElement sensorElem = mSensorManager->
writeXml( *doc );
3471 qgisNode.appendChild( sensorElem );
3475 const QDomElement viewSettingsElem = mViewSettings->
writeXml( *doc, context );
3476 qgisNode.appendChild( viewSettingsElem );
3480 const QDomElement styleSettingsElem = mStyleSettings->
writeXml( *doc, context );
3481 qgisNode.appendChild( styleSettingsElem );
3485 const QDomElement timeSettingsElement = mTimeSettings->
writeXml( *doc, context );
3486 qgisNode.appendChild( timeSettingsElement );
3490 const QDomElement elevationPropertiesElement = mElevationProperties->
writeXml( *doc, context );
3491 qgisNode.appendChild( elevationPropertiesElement );
3495 const QDomElement displaySettingsElem = mDisplaySettings->
writeXml( *doc, context );
3496 qgisNode.appendChild( displaySettingsElem );
3500 const QDomElement gpsSettingsElem = mGpsSettings->
writeXml( *doc, context );
3501 qgisNode.appendChild( gpsSettingsElem );
3510 QFile backupFile( QStringLiteral(
"%1~" ).arg( filename ) );
3512 ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
3513 ok &= projectFile.open( QIODevice::ReadOnly );
3516 while ( ok && !projectFile.atEnd() )
3518 ba = projectFile.read( 10240 );
3519 ok &= backupFile.write( ba ) == ba.size();
3522 projectFile.close();
3527 setError( tr(
"Unable to create backup file %1" ).arg( backupFile.fileName() ) );
3532 struct utimbuf tb = {
static_cast<time_t
>( fi.lastRead().toSecsSinceEpoch() ),
static_cast<time_t
>( fi.lastModified().toSecsSinceEpoch() ) };
3533 utime( backupFile.fileName().toUtf8().constData(), &tb );
3536 if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3538 projectFile.close();
3541 setError( tr(
"Unable to save to file %1" ).arg( projectFile.fileName() ) );
3545 QTemporaryFile tempFile;
3546 bool ok = tempFile.open();
3549 QTextStream projectFileStream( &tempFile );
3550 doc->save( projectFileStream, 2 );
3551 ok &= projectFileStream.pos() > -1;
3553 ok &= tempFile.seek( 0 );
3556 while ( ok && !tempFile.atEnd() )
3558 ba = tempFile.read( 10240 );
3559 ok &= projectFile.write( ba ) == ba.size();
3562 ok &= projectFile.error() == QFile::NoError;
3564 projectFile.close();
3571 setError( tr(
"Unable to save to file %1. Your project "
3572 "may be corrupted on disk. Try clearing some space on the volume and "
3573 "check file permissions before pressing save again." )
3574 .arg( projectFile.fileName() ) );
3588 bool propertiesModified;
3589 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3591 if ( propertiesModified )
3601 bool propertiesModified;
3602 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3604 if ( propertiesModified )
3614 bool propertiesModified;
3615 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3617 if ( propertiesModified )
3627 bool propertiesModified;
3628 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3630 if ( propertiesModified )
3640 bool propertiesModified;
3641 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3643 if ( propertiesModified )
3651 const QStringList &def,
3663 value =
property->value();
3665 const bool valid = QMetaType::Type::QStringList == value.userType();
3671 return value.toStringList();
3694 value =
property->value();
3696 const bool valid = value.canConvert( QMetaType::Type::QString );
3701 return value.toString();
3720 value =
property->value();
3723 const bool valid = value.canConvert( QMetaType::Type::Int );
3732 return value.toInt();
3747 const QVariant value =
property->value();
3749 const bool valid = value.canConvert( QMetaType::Type::Double );
3754 return value.toDouble();
3771 const QVariant value =
property->value();
3773 const bool valid = value.canConvert( QMetaType::Type::Bool );
3778 return value.toBool();
3790 if (
findKey_( scope, key, mProperties ) )
3796 return !
findKey_( scope, key, mProperties );
3805 QStringList entries;
3807 if ( foundProperty )
3824 QStringList entries;
3826 if ( foundProperty )
3841 dump_( mProperties );
3861 filePath = storage->filePath( mFile.fileName() );
3888void QgsProject::setError(
const QString &errorMessage )
3892 mErrorMessage = errorMessage;
3899 return mErrorMessage;
3902void QgsProject::clearError()
3906 setError( QString() );
3913 delete mBadLayerHandler;
3914 mBadLayerHandler = handler;
3921 const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find(
id );
3922 if ( it == mEmbeddedLayers.constEnd() )
3926 return it.value().first;
3936 static QString sPrevProjectFilePath;
3937 static QDateTime sPrevProjectFileTimestamp;
3938 static QDomDocument sProjectDocument;
3940 QString qgsProjectFile = projectFilePath;
3942 if ( projectFilePath.endsWith( QLatin1String(
".qgz" ), Qt::CaseInsensitive ) )
3944 archive.
unzip( projectFilePath );
3948 const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
3950 if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
3952 sPrevProjectFilePath.clear();
3954 QFile projectFile( qgsProjectFile );
3955 if ( !projectFile.open( QIODevice::ReadOnly ) )
3960 if ( !sProjectDocument.setContent( &projectFile ) )
3965 sPrevProjectFilePath = projectFilePath;
3966 sPrevProjectFileTimestamp = projectFileTimestamp;
3970 bool useAbsolutePaths =
true;
3972 const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral(
"properties" ) );
3973 if ( !propertiesElem.isNull() )
3975 const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral(
"Paths" ) ).firstChildElement( QStringLiteral(
"Absolute" ) );
3976 if ( !absElem.isNull() )
3978 useAbsolutePaths = absElem.text().compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
3983 if ( !useAbsolutePaths )
3988 const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral(
"projectlayers" ) );
3989 if ( projectLayersElem.isNull() )
3994 QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral(
"maplayer" ) );
3995 while ( ! mapLayerElem.isNull() )
3998 const QString
id = mapLayerElem.firstChildElement( QStringLiteral(
"id" ) ).text();
3999 if (
id == layerId )
4002 if ( mapLayerElem.attribute( QStringLiteral(
"embedded" ) ) == QLatin1String(
"1" ) )
4007 mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
4009 if ( addLayer( mapLayerElem, brokenNodes, embeddedContext,
flags ) )
4015 mEmbeddedLayers.remove( layerId );
4019 mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral(
"maplayer" ) );
4029 QString qgsProjectFile = projectFilePath;
4031 if ( projectFilePath.endsWith( QLatin1String(
".qgz" ), Qt::CaseInsensitive ) )
4033 archive.
unzip( projectFilePath );
4038 QFile projectFile( qgsProjectFile );
4039 if ( !projectFile.open( QIODevice::ReadOnly ) )
4044 QDomDocument projectDocument;
4045 if ( !projectDocument.setContent( &projectFile ) )
4057 QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral(
"layer-tree-group" ) );
4058 if ( !layerTreeElem.isNull() )
4068 if ( !group || group->
customProperty( QStringLiteral(
"embedded" ) ).toBool() )
4081 newGroup->
setCustomProperty( QStringLiteral(
"embedded_project" ), projectFilePath );
4084 mLayerTreeRegistryBridge->
setEnabled(
false );
4085 initializeEmbeddedSubtree( projectFilePath, newGroup,
flags );
4086 mLayerTreeRegistryBridge->
setEnabled(
true );
4089 const auto constFindLayerIds = newGroup->
findLayerIds();
4090 for (
const QString &layerId : constFindLayerIds )
4107 const auto constChildren = group->
children();
4111 child->setCustomProperty( QStringLiteral(
"embedded" ), 1 );
4120 QList<QDomNode> brokenNodes;
4144 writeEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
4152 return readNumEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/TopologicalEditing" ), 0 );
4159 if ( mDistanceUnits == unit )
4162 mDistanceUnits = unit;
4171 if ( mAreaUnits == unit )
4184 if ( !mCachedHomePath.isEmpty() )
4185 return mCachedHomePath;
4189 if ( !mHomePath.isEmpty() )
4191 const QFileInfo homeInfo( mHomePath );
4192 if ( !homeInfo.isRelative() )
4194 mCachedHomePath = mHomePath;
4204 const QString storagePath { storage->filePath(
fileName() ) };
4205 if ( ! storagePath.isEmpty() && QFileInfo::exists( storagePath ) )
4207 mCachedHomePath = QFileInfo( storagePath ).path();
4208 return mCachedHomePath;
4212 mCachedHomePath = pfi.path();
4213 return mCachedHomePath;
4216 if ( !pfi.exists() )
4218 mCachedHomePath = mHomePath;
4222 if ( !mHomePath.isEmpty() )
4225 mCachedHomePath = QDir::cleanPath( pfi.path() +
'/' + mHomePath );
4229 mCachedHomePath = pfi.canonicalPath();
4231 return mCachedHomePath;
4246 return mRelationManager;
4253 return mLayoutManager.get();
4260 return mLayoutManager.get();
4267 return m3DViewsManager.get();
4274 return m3DViewsManager.get();
4281 return mBookmarkManager;
4288 return mBookmarkManager;
4295 return mSensorManager;
4302 return mSensorManager;
4309 return mViewSettings;
4316 return mViewSettings;
4323 return mStyleSettings;
4331 return mStyleSettings;
4338 return mTimeSettings;
4345 return mTimeSettings;
4352 return mElevationProperties;
4359 return mElevationProperties;
4366 return mDisplaySettings;
4373 return mDisplaySettings;
4380 return mGpsSettings;
4387 return mGpsSettings;
4401 return mMapThemeCollection.get();
4408 return mAnnotationManager.get();
4415 return mAnnotationManager.get();
4422 const QMap<QString, QgsMapLayer *> &projectLayers =
mapLayers();
4423 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4428 if (
layers.contains( it.value() ) )
4429 it.value()->setFlags( it.value()->flags() &
~QgsMapLayer::Identifiable );
4445 for (
const QString &layerId : layerIds )
4463 for ( QMap<QString, QgsMapLayer *>::const_iterator it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
4497 updateTransactionGroups();
4504 return mTransactionMode;
4515 const auto constLayers =
mapLayers().values();
4518 if ( layer->isEditable() )
4520 QgsLogger::warning( tr(
"Transaction mode can be changed only if all layers are not editable." ) );
4526 updateTransactionGroups();
4535 return mTransactionGroups;
4548 return mLayerStore->count();
4555 return mLayerStore->validCount();
4563 return mLayerStore->mapLayer( layerId );
4570 return mLayerStore->mapLayersByName( layerName );
4577 QList<QgsMapLayer *>
layers;
4578 const auto constMapLayers { mLayerStore->mapLayers() };
4579 for (
const auto &l : constMapLayers )
4581 if ( ! l->serverProperties()->shortName().isEmpty() )
4583 if ( l->serverProperties()->shortName() == shortName )
4586 else if ( l->name() == shortName )
4602 if ( !archive->unzip( filename ) )
4604 setError( tr(
"Unable to unzip file '%1'" ).arg( filename ) );
4609 if ( archive->projectFile().isEmpty() )
4611 setError( tr(
"Zip archive does not provide a project file" ) );
4616 releaseHandlesToProjectArchive();
4617 mArchive = std::move( archive );
4634 setError( tr(
"Cannot read unzipped qgs project file" ) + QStringLiteral(
": " ) +
error() );
4644bool QgsProject::zip(
const QString &filename )
4652 const QString
baseName = QFileInfo( filename ).baseName();
4653 const QString qgsFileName = QStringLiteral(
"%1.qgs" ).arg(
baseName );
4654 QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
4656 bool writeOk =
false;
4657 if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
4659 writeOk = writeProjectFile( qgsFile.fileName() );
4666 setError( tr(
"Unable to write temporary qgs file" ) );
4671 const QFileInfo info( qgsFile );
4673 const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
4675 bool auxiliaryStorageSavedOk =
true;
4676 if ( ! saveAuxiliaryStorage( asFileName ) )
4678 const QString err = mAuxiliaryStorage->errorString();
4679 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 ) );
4680 auxiliaryStorageSavedOk =
false;
4683 if ( !mArchive->exists() )
4685 releaseHandlesToProjectArchive();
4687 mArchive->unzip( mFile.fileName() );
4690 const QString auxiliaryStorageFile =
static_cast<QgsProjectArchive *
>( mArchive.get() )->auxiliaryStorageFile();
4691 if ( ! auxiliaryStorageFile.isEmpty() )
4693 archive->
addFile( auxiliaryStorageFile );
4702 if ( QFile::exists( asFileName ) )
4704 archive->addFile( asFileName );
4709 archive->addFile( qgsFile.fileName() );
4712 const QStringList &
files = mArchive->files();
4713 for (
const QString &file :
files )
4715 if ( !file.endsWith( QLatin1String(
".qgs" ), Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
4717 archive->addFile( file );
4723 if ( !archive->zip( filename ) )
4725 setError( tr(
"Unable to perform zip" ) );
4729 return auxiliaryStorageSavedOk && zipOk;
4740 const QList<QgsMapLayer *> &layers,
4742 bool takeOwnership )
4746 const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers(
layers, takeOwnership ) };
4747 if ( !myResultList.isEmpty() )
4750 for (
auto &l : myResultList )
4760 if ( mAuxiliaryStorage )
4775 mProjectScope.reset();
4777 return myResultList;
4783 bool takeOwnership )
4787 QList<QgsMapLayer *> addedLayers;
4788 addedLayers =
addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
4789 return addedLayers.isEmpty() ? nullptr : addedLayers[0];
4792void QgsProject::removeAuxiliaryLayer(
const QgsMapLayer *ml )
4799 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( ml );
4811 for (
const auto &layerId : layerIds )
4812 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4814 mProjectScope.reset();
4815 mLayerStore->removeMapLayers( layerIds );
4822 for (
const auto &layer :
layers )
4823 removeAuxiliaryLayer( layer );
4825 mProjectScope.reset();
4826 mLayerStore->removeMapLayers(
layers );
4833 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4834 mProjectScope.reset();
4835 mLayerStore->removeMapLayer( layerId );
4842 removeAuxiliaryLayer( layer );
4843 mProjectScope.reset();
4844 mLayerStore->removeMapLayer( layer );
4851 mProjectScope.reset();
4852 return mLayerStore->takeMapLayer( layer );
4859 return mMainAnnotationLayer;
4866 if ( mLayerStore->count() == 0 )
4869 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
4870 mProjectScope.reset();
4871 mLayerStore->removeAllMapLayers();
4873 snapSingleBlocker.release();
4875 if ( !mBlockSnappingUpdates )
4883 const QMap<QString, QgsMapLayer *>
layers = mLayerStore->mapLayers();
4884 QMap<QString, QgsMapLayer *>::const_iterator it =
layers.constBegin();
4885 for ( ; it !=
layers.constEnd(); ++it )
4887 it.value()->reload();
4896 return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
4903 return mTransactionGroups.value( qMakePair( providerKey, connString ) );
4910 return &mEditBufferGroup;
4921 if ( mSettings.
value( QStringLiteral(
"/projections/unknownCrsBehavior" ), QStringLiteral(
"NoAction" ),
QgsSettings::App ).toString() == QStringLiteral(
"UseProjectCrs" )
4922 || mSettings.
value( QStringLiteral(
"/projections/unknownCrsBehavior" ), 0,
QgsSettings::App ).toString() == QLatin1String(
"2" ) )
4930 const QString layerDefaultCrs = mSettings.
value( QStringLiteral(
"/Projections/layerDefaultCrs" ),
geoEpsgCrsAuthId() ).toString();
4951bool QgsProject::saveAuxiliaryStorage(
const QString &filename )
4957 for (
auto it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
4962 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
4970 if ( !mAuxiliaryStorage->exists( *
this ) && empty )
4974 else if ( !filename.isEmpty() )
4976 return mAuxiliaryStorage->saveAs( filename );
4980 return mAuxiliaryStorage->saveAs( *
this );
4993 return sPropertyDefinitions;
5006 return mAuxiliaryStorage.get();
5013 return mAuxiliaryStorage.get();
5020 const QDir archiveDir( mArchive->dir() );
5021 QTemporaryFile tmpFile( archiveDir.filePath(
"XXXXXX_" + nameTemplate ),
this );
5022 tmpFile.setAutoRemove(
false );
5024 mArchive->addFile( tmpFile.fileName() );
5025 return tmpFile.fileName();
5032 QStringList attachments;
5034 const QStringList files = mArchive->files();
5035 attachments.reserve( files.size() );
5036 for (
const QString &file : files )
5038 if ( QFileInfo( file ).baseName() !=
baseName )
5040 attachments.append( file );
5050 return mArchive->removeFile( path );
5057 return QStringLiteral(
"attachment:///%1" ).arg( QFileInfo( attachedFile ).
fileName() );
5064 if ( identifier.startsWith( QLatin1String(
"attachment:///" ) ) )
5066 return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
5087 mProjectScope.reset();
5101 for ( QMap<QString, QgsMapLayer *>::const_iterator it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
5115 const QMap<QString, QgsMapLayer *> &projectLayers =
mapLayers();
5116 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
5121 if (
layers.contains( it.value() ) )
5122 it.value()->setFlags( it.value()->flags() &
~QgsMapLayer::Removable );
5133 QStringList customColors;
5134 QStringList customColorLabels;
5136 QgsNamedColorList::const_iterator colorIt = colors.constBegin();
5137 for ( ; colorIt != colors.constEnd(); ++colorIt )
5140 const QString label = ( *colorIt ).second;
5141 customColors.append( color );
5142 customColorLabels.append( label );
5144 writeEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Colors" ), customColors );
5145 writeEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Labels" ), customColorLabels );
5146 mProjectScope.reset();
5154 if ( mBackgroundColor == color )
5157 mBackgroundColor = color;
5165 return mBackgroundColor;
5172 if ( mSelectionColor == color )
5175 mSelectionColor = color;
5183 return mSelectionColor;
5220 translationContext.setFileName( QStringLiteral(
"%1/%2.ts" ).arg(
absolutePath(),
baseName() ) );
5224 translationContext.writeTsFile( locale );
5227QString
QgsProject::translate(
const QString &context,
const QString &sourceText,
const char *disambiguation,
int n )
const
5236 QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
5238 if ( result.isEmpty() )
5252 for (
auto it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
5257 if ( !( ( *it )->accept( visitor ) ) )
5266 if ( !mLayoutManager->accept( visitor ) )
5269 if ( !mAnnotationManager->accept( visitor ) )
5277 return mElevationShadingRenderer;
5280void QgsProject::loadProjectFlags(
const QDomDocument *doc )
5284 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral(
"projectFlags" ) );
5286 if ( !element.isNull() )
5293 element = doc->documentElement().firstChildElement( QStringLiteral(
"evaluateDefaultValues" ) );
5294 if ( !element.isNull() )
5296 if ( element.attribute( QStringLiteral(
"active" ), QStringLiteral(
"0" ) ).toInt() == 1 )
5301 element = doc->documentElement().firstChildElement( QStringLiteral(
"trust" ) );
5302 if ( !element.isNull() )
5304 if ( element.attribute( QStringLiteral(
"active" ), QStringLiteral(
"0" ) ).toInt() == 1 )
5320 const QString projectFunctions =
readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString() );
5321 if ( !projectFunctions.isEmpty() )
5341QHash< QString, QColor > loadColorsFromProject(
const QgsProject *project )
5343 QHash< QString, QColor > colors;
5346 QStringList colorStrings = project->
readListEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Colors" ) );
5347 const QStringList colorLabels = project->
readListEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Labels" ) );
5351 for ( QStringList::iterator it = colorStrings.begin();
5352 it != colorStrings.end(); ++it )
5356 if ( colorLabels.length() > colorIndex )
5358 label = colorLabels.at( colorIndex );
5361 colors.insert( label.toLower(), color );
5369GetNamedProjectColor::GetNamedProjectColor(
const QgsProject *project )
5375 mColors = loadColorsFromProject( project );
5378GetNamedProjectColor::GetNamedProjectColor(
const QHash<QString, QColor> &colors )
5386 const QString colorName = values.at( 0 ).toString().toLower();
5387 if ( mColors.contains( colorName ) )
5389 return QStringLiteral(
"%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
5397 return new GetNamedProjectColor( mColors );
5400GetNamedProjectColorObject::GetNamedProjectColorObject(
const QgsProject *project )
5406 mColors = loadColorsFromProject( project );
5409GetNamedProjectColorObject::GetNamedProjectColorObject(
const QHash<QString, QColor> &colors )
5417 const QString colorName = values.at( 0 ).toString().toLower();
5418 if ( mColors.contains( colorName ) )
5420 return mColors.value( colorName );
5428 return new GetNamedProjectColorObject( mColors );
5433GetSensorData::GetSensorData(
const QMap<QString, QgsAbstractSensor::SensorData> &sensorData )
5436 QStringLiteral(
"Sensors" ) )
5437 , mSensorData( sensorData )
5443 const QString sensorName = values.at( 0 ).toString();
5444 const int expiration = values.at( 1 ).toInt();
5445 const qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
5446 if ( mSensorData.contains( sensorName ) )
5448 if ( expiration <= 0 || ( timestamp - mSensorData[sensorName].lastTimestamp.toMSecsSinceEpoch() ) < expiration )
5450 return mSensorData[sensorName].lastValue;
5459 return new GetSensorData( mSensorData );
@ DontLoad3DViews
Skip loading 3D views.
@ 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.
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ DontUpgradeAnnotations
Don't upgrade old annotation items to QgsAnnotationItem.
@ 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.
QFlags< ProjectCapability > ProjectCapabilities
Flags which control project capabilities.
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
DistanceUnit
Units of distance.
FilePathType
File path types.
TransactionMode
Transaction mode.
@ 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.
@ SquareMeters
Square meters.
@ Critical
Critical/error message.
@ Success
Used for reporting a successful operation.
PythonEmbeddedMode
Authorisation to run Python Embedded in projects.
@ Always
Python embedded is always run.
@ Ask
User is prompt before running.
@ SessionOnly
Only during this session.
@ Compound
Compound (horizontal + vertical) CRS.
@ Projected
Projected CRS.
@ DerivedProjected
Derived projected CRS.
@ Engineering
Engineering CRS.
@ Geographic3d
3D geopraphic CRS
@ Geographic2d
2D geographic CRS
@ Geocentric
Geocentric CRS.
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
@ 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.
@ 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...
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
LayerType
Types of layers that can be added to a map.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ SkipCredentialsRequest
Skip credentials if the provided one are not valid, let the provider be invalid, avoiding to block th...
@ ParallelThreadLoading
Provider is created in a parallel thread than the one where it will live.
QFlags< ProjectFlag > ProjectFlags
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
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 resolveReferences(QgsProject *project) override
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects.
void setTransformContext(const QgsCoordinateTransformContext &context) override
Sets the coordinate transform context to transformContext.
void reset()
Resets the annotation layer to a default state, and clears all items from it.
bool isEmpty() const
Returns true if the annotation layer is empty and contains no annotations.
Manages storage of a set of QgsAnnotation annotation objects.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
static const QgsSettingsEntryString * settingsLocaleUserLocale
Settings entry locale user locale.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ....
static QString userFullName()
Returns the user's operating system login account full display name.
static QString userLoginName()
Returns the user's operating system login account name.
Class allowing to manage the zip/unzip actions.
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.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool hasVerticalAxis() const
Returns true if the CRS has a vertical axis.
QString toProj() const
Returns a Proj string representation of this CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs, QString &error)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
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.
QgsCoordinateReferenceSystem verticalCrs() const
Returns the vertical CRS associated with this CRS object.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
Qgis::CrsType type() const
Returns the type of the CRS.
Qgis::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
void readSettings()
Reads the context's state from application settings.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context's state to a DOM element.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context's state from a DOM element.
Abstract base class for spatial data provider implementations.
@ 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.
This class can render elevation shading on an image with different methods (eye dome lighting,...
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes configuration on a DOM element.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads configuration from a DOM element.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A abstract base class for defining QgsExpression functions.
An expression node for expression functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
Stores global configuration for labeling engine.
Class used to work with layer dependencies stored in a XML project or layer definition file.
Layer tree group node serves as a container for layers and further groups.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QList< QgsLayerTreeGroup * > findGroups(bool recursive=false) const
Find group layer nodes.
QString name() const override
Returns the group's name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already)
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the children, disconnect all the forwarded and external signals and sets their parent to null...
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &contex