QGIS API Documentation  2.99.0-Master (7fe5405)
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"
21 #include "qgsexception.h"
22 #include "qgslayertree.h"
23 #include "qgslayertreeutils.h"
25 #include "qgslogger.h"
26 #include "qgsmessagelog.h"
27 #include "qgspluginlayer.h"
28 #include "qgspluginlayerregistry.h"
30 #include "qgssnappingconfig.h"
31 #include "qgsprojectversion.h"
32 #include "qgsrasterlayer.h"
33 #include "qgsrectangle.h"
34 #include "qgsrelationmanager.h"
35 #include "qgsvectorlayer.h"
36 #include "qgsmapthemecollection.h"
37 #include "qgslayerdefinition.h"
38 #include "qgsunittypes.h"
39 #include "qgstransaction.h"
40 #include "qgstransactiongroup.h"
41 #include "qgsvectordataprovider.h"
43 
44 #include <QApplication>
45 #include <QFileInfo>
46 #include <QDomNode>
47 #include <QObject>
48 #include <QTextStream>
49 #include <QTemporaryFile>
50 #include <QDir>
51 #include <QUrl>
52 #include <QSettings>
53 
54 #ifdef Q_OS_UNIX
55 #include <utime.h>
56 #elif _MSC_VER
57 #include <sys/utime.h>
58 #endif
59 
60 // canonical project instance
61 QgsProject *QgsProject::sProject = nullptr;
62 
71 QStringList makeKeyTokens_( const QString& scope, const QString& key )
72 {
73  QStringList keyTokens = QStringList( scope );
74  keyTokens += key.split( '/', QString::SkipEmptyParts );
75 
76  // be sure to include the canonical root node
77  keyTokens.push_front( QStringLiteral( "properties" ) );
78 
79  //check validy of keys since an unvalid xml name will will be dropped upon saving the xml file. If not valid, we print a message to the console.
80  for ( int i = 0; i < keyTokens.size(); ++i )
81  {
82  QString keyToken = keyTokens.at( i );
83 
84  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
85  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
86  QString nameCharRegexp = QStringLiteral( "[^:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x2FF\\x370-\\x37D\\x37F-\\x1FFF\\x200C-\\x200D\\x2070-\\x218F\\x2C00-\\x2FEF\\x3001-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFFD\\-\\.0-9\\xB7\\x0300-\\x036F\\x203F-\\x2040]" );
87  QString nameStartCharRegexp = QStringLiteral( "^[^:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x2FF\\x370-\\x37D\\x37F-\\x1FFF\\x200C-\\x200D\\x2070-\\x218F\\x2C00-\\x2FEF\\x3001-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFFD]" );
88 
89  if ( keyToken.contains( QRegExp( nameCharRegexp ) ) || keyToken.contains( QRegExp( nameStartCharRegexp ) ) )
90  {
91 
92  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
93  QgsMessageLog::logMessage( errorString, QString::null, QgsMessageLog::CRITICAL );
94 
95  }
96 
97  }
98 
99  return keyTokens;
100 }
101 
102 
103 
113 QgsProjectProperty* findKey_( const QString& scope,
114  const QString& key,
115  QgsProjectPropertyKey& rootProperty )
116 {
117  QgsProjectPropertyKey* currentProperty = &rootProperty;
118  QgsProjectProperty* nextProperty; // link to next property down hierarchy
119 
120  QStringList keySequence = makeKeyTokens_( scope, key );
121 
122  while ( !keySequence.isEmpty() )
123  {
124  // if the current head of the sequence list matches the property name,
125  // then traverse down the property hierarchy
126  if ( keySequence.first() == currentProperty->name() )
127  {
128  // remove front key since we're traversing down a level
129  keySequence.pop_front();
130 
131  if ( 1 == keySequence.count() )
132  {
133  // if we have only one key name left, then return the key found
134  return currentProperty->find( keySequence.front() );
135  }
136  else if ( keySequence.isEmpty() )
137  {
138  // if we're out of keys then the current property is the one we
139  // want; i.e., we're in the rate case of being at the top-most
140  // property node
141  return currentProperty;
142  }
143  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
144  {
145  if ( nextProperty->isKey() )
146  {
147  currentProperty = static_cast<QgsProjectPropertyKey*>( nextProperty );
148  }
149  else if ( nextProperty->isValue() && 1 == keySequence.count() )
150  {
151  // it may be that this may be one of several property value
152  // nodes keyed by QDict string; if this is the last remaining
153  // key token and the next property is a value node, then
154  // that's the situation, so return the currentProperty
155  return currentProperty;
156  }
157  else
158  {
159  // QgsProjectPropertyValue not Key, so return null
160  return nullptr;
161  }
162  }
163  else
164  {
165  // if the next key down isn't found
166  // then the overall key sequence doesn't exist
167  return nullptr;
168  }
169  }
170  else
171  {
172  return nullptr;
173  }
174  }
175 
176  return nullptr;
177 }
178 
179 
180 
188 QgsProjectProperty *addKey_( const QString& scope,
189  const QString& key,
190  QgsProjectPropertyKey* rootProperty,
191  const QVariant& value )
192 {
193  QStringList keySequence = makeKeyTokens_( scope, key );
194 
195  // cursor through property key/value hierarchy
196  QgsProjectPropertyKey *currentProperty = rootProperty;
197  QgsProjectProperty *nextProperty; // link to next property down hierarchy
198  QgsProjectPropertyKey* newPropertyKey;
199 
200  while ( ! keySequence.isEmpty() )
201  {
202  // if the current head of the sequence list matches the property name,
203  // then traverse down the property hierarchy
204  if ( keySequence.first() == currentProperty->name() )
205  {
206  // remove front key since we're traversing down a level
207  keySequence.pop_front();
208 
209  // if key sequence has one last element, then we use that as the
210  // name to store the value
211  if ( 1 == keySequence.count() )
212  {
213  currentProperty->setValue( keySequence.front(), value );
214  return currentProperty;
215  }
216  // we're at the top element if popping the keySequence element
217  // will leave it empty; in that case, just add the key
218  else if ( keySequence.isEmpty() )
219  {
220  currentProperty->setValue( value );
221 
222  return currentProperty;
223  }
224  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
225  {
226  currentProperty = dynamic_cast<QgsProjectPropertyKey*>( nextProperty );
227 
228  if ( currentProperty )
229  {
230  continue;
231  }
232  else // QgsProjectPropertyValue not Key, so return null
233  {
234  return nullptr;
235  }
236  }
237  else // the next subkey doesn't exist, so add it
238  {
239  if (( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
240  {
241  currentProperty = newPropertyKey;
242  }
243  continue;
244  }
245  }
246  else
247  {
248  return nullptr;
249  }
250  }
251 
252  return nullptr;
253 
254 }
255 
256 
257 void removeKey_( const QString& scope,
258  const QString& key,
259  QgsProjectPropertyKey &rootProperty )
260 {
261  QgsProjectPropertyKey *currentProperty = &rootProperty;
262 
263  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
264  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
265 
266  QStringList keySequence = makeKeyTokens_( scope, key );
267 
268  while ( ! keySequence.isEmpty() )
269  {
270  // if the current head of the sequence list matches the property name,
271  // then traverse down the property hierarchy
272  if ( keySequence.first() == currentProperty->name() )
273  {
274  // remove front key since we're traversing down a level
275  keySequence.pop_front();
276 
277  // if we have only one key name left, then try to remove the key
278  // with that name
279  if ( 1 == keySequence.count() )
280  {
281  currentProperty->removeKey( keySequence.front() );
282  }
283  // if we're out of keys then the current property is the one we
284  // want to remove, but we can't delete it directly; we need to
285  // delete it from the parent property key container
286  else if ( keySequence.isEmpty() )
287  {
288  previousQgsPropertyKey->removeKey( currentProperty->name() );
289  }
290  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
291  {
292  previousQgsPropertyKey = currentProperty;
293  currentProperty = dynamic_cast<QgsProjectPropertyKey*>( nextProperty );
294 
295  if ( currentProperty )
296  {
297  continue;
298  }
299  else // QgsProjectPropertyValue not Key, so return null
300  {
301  return;
302  }
303  }
304  else // if the next key down isn't found
305  { // then the overall key sequence doesn't exist
306  return;
307  }
308  }
309  else
310  {
311  return;
312  }
313  }
314 
315 }
316 
317 QgsProject::QgsProject( QObject* parent )
318  : QObject( parent )
319  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
320  , mSnappingConfig( this )
321  , mRelationManager( new QgsRelationManager( this ) )
322  , mRootGroup( new QgsLayerTreeGroup )
323  , mAutoTransaction( false )
324  , mEvaluateDefaultValues( false )
325  , mDirty( false )
326 {
327  mProperties.setName( QStringLiteral( "properties" ) );
328  clear();
329 
330  // bind the layer tree to the map layer registry.
331  // whenever layers are added to or removed from the registry,
332  // layer tree will be updated
333  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
334  connect( this, SIGNAL( layersAdded( QList<QgsMapLayer*> ) ), this, SLOT( onMapLayersAdded( QList<QgsMapLayer*> ) ) );
335  connect( this, SIGNAL( layersRemoved( QStringList ) ), this, SLOT( cleanTransactionGroups() ) );
336  connect( this, SIGNAL( layersWillBeRemoved( QList<QgsMapLayer*> ) ), this, SLOT( onMapLayersRemoved( QList<QgsMapLayer*> ) ) );
337 }
338 
339 
341 {
342  delete mBadLayerHandler;
343  delete mRelationManager;
344  delete mRootGroup;
345 
347 }
348 
349 
351 {
352  if ( !sProject )
353  {
354  sProject = new QgsProject;
355  }
356  return sProject;
357 }
358 
359 void QgsProject::setTitle( const QString& title )
360 {
361  if ( title == mTitle )
362  return;
363 
364  mTitle = title;
365 
366  setDirty( true );
367 }
368 
369 
370 QString QgsProject::title() const
371 {
372  return mTitle;
373 }
374 
375 
377 {
378  return mDirty;
379 }
380 
381 void QgsProject::setDirty( bool b )
382 {
383  mDirty = b;
384 }
385 
386 void QgsProject::setFileName( const QString& name )
387 {
388  if ( name == mFile.fileName() )
389  return;
390 
391  QString oldHomePath = homePath();
392 
393  mFile.setFileName( name );
394  emit fileNameChanged();
395 
396  QString newHomePath = homePath();
397  if ( newHomePath != oldHomePath )
398  emit homePathChanged();
399 
400  setDirty( true );
401 }
402 
403 QString QgsProject::fileName() const
404 {
405  return mFile.fileName();
406 }
407 
408 QFileInfo QgsProject::fileInfo() const
409 {
410  return QFileInfo( mFile );
411 }
412 
414 {
415  QgsCoordinateReferenceSystem projectCrs;
416  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
417  if ( currentCRS != -1 )
418  {
419  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
420  }
421  return projectCrs;
422 }
423 
425 {
426  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ), crs.toProj4() );
427  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), static_cast< int >( crs.srsid() ) );
428  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ), crs.authid() );
429  setDirty( true );
430 }
431 
432 QString QgsProject::ellipsoid() const
433 {
434  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), GEO_NONE );
435 }
436 
437 void QgsProject::setEllipsoid( const QString& ellipsoid )
438 {
439  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
440  setDirty( true );
441 }
442 
444 {
445  mFile.setFileName( QString() );
446  mProperties.clearKeys();
447  mTitle.clear();
448  mAutoTransaction = false;
449  mEvaluateDefaultValues = false;
450  mDirty = false;
451  mCustomVariables.clear();
452 
453  mEmbeddedLayers.clear();
454  mRelationManager->clear();
455  mSnappingConfig.reset();
456  emit snappingConfigChanged();
457 
458  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
460 
461  mRootGroup->removeAllChildren();
462 
463  // reset some default project properties
464  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
465  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
466  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
467  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
468 
469  //copy default units to project
470  QSettings s;
471  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
472  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
473 
474  setDirty( false );
475 }
476 
477 // basically a debugging tool to dump property list values
478 void dump_( const QgsProjectPropertyKey& topQgsPropertyKey )
479 {
480  QgsDebugMsg( "current properties:" );
481  topQgsPropertyKey.dump();
482 }
483 
484 
515 void _getProperties( const QDomDocument& doc, QgsProjectPropertyKey& project_properties )
516 {
517  QDomNodeList properties = doc.elementsByTagName( QStringLiteral( "properties" ) );
518 
519  if ( properties.count() > 1 )
520  {
521  QgsDebugMsg( "there appears to be more than one ``properties'' XML tag ... bailing" );
522  return;
523  }
524  else if ( properties.count() < 1 ) // no properties found, so we're done
525  {
526  return;
527  }
528 
529  // item(0) because there should only be ONE "properties" node
530  QDomNodeList scopes = properties.item( 0 ).childNodes();
531 
532  if ( scopes.count() < 1 )
533  {
534  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
535  return;
536  }
537 
538  QDomNode propertyNode = properties.item( 0 );
539 
540  if ( ! project_properties.readXml( propertyNode ) )
541  {
542  QgsDebugMsg( "Project_properties.readXml() failed" );
543  }
544 }
545 
546 
551 static void _getTitle( const QDomDocument& doc, QString& title )
552 {
553  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
554 
555  title = QLatin1String( "" ); // by default the title will be empty
556 
557  if ( !nl.count() )
558  {
559  QgsDebugMsg( "unable to find title element" );
560  return;
561  }
562 
563  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element ok
564 
565  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
566  {
567  QgsDebugMsg( "unable to find title element" );
568  return;
569  }
570 
571  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
572 
573  if ( !titleTextNode.isText() )
574  {
575  QgsDebugMsg( "unable to find title element" );
576  return;
577  }
578 
579  QDomText titleText = titleTextNode.toText();
580 
581  title = titleText.data();
582 
583 }
584 
585 QgsProjectVersion getVersion( const QDomDocument& doc )
586 {
587  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
588 
589  if ( !nl.count() )
590  {
591  QgsDebugMsg( " unable to find qgis element in project file" );
592  return QgsProjectVersion( 0, 0, 0, QString() );
593  }
594 
595  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
596 
597  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
598  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
599  return projectVersion;
600 }
601 
602 void QgsProject::processLayerJoins( QgsVectorLayer* layer )
603 {
604  if ( !layer )
605  return;
606 
607  layer->createJoinCaches();
608  layer->updateFields();
609 }
610 
612 {
613  return mSnappingConfig;
614 }
615 
617 {
618  if ( mSnappingConfig == snappingConfig )
619  return;
620 
621  mSnappingConfig = snappingConfig;
622  setDirty();
623  emit snappingConfigChanged();
624 }
625 
626 bool QgsProject::_getMapLayers( const QDomDocument& doc, QList<QDomNode>& brokenNodes )
627 {
628  // Layer order is set by the restoring the legend settings from project file.
629  // This is done on the 'readProject( ... )' signal
630 
631  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
632 
633  // process the map layer nodes
634 
635  if ( 0 == nl.count() ) // if we have no layers to process, bail
636  {
637  return true; // Decided to return "true" since it's
638  // possible for there to be a project with no
639  // layers; but also, more imporantly, this
640  // would cause the tests/qgsproject to fail
641  // since the test suite doesn't currently
642  // support test layers
643  }
644 
645  bool returnStatus = true;
646 
647  emit layerLoaded( 0, nl.count() );
648 
649  // order layers based on their dependencies
650  QgsLayerDefinition::DependencySorter depSorter( doc );
651  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
652  return false;
653 
654  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
655 
656  // Collect vector layers with joins.
657  // They need to refresh join caches and symbology infos after all layers are loaded
658  QList< QPair< QgsVectorLayer*, QDomElement > > vLayerList;
659  int i = 0;
660  Q_FOREACH ( const QDomNode& node, sortedLayerNodes )
661  {
662  QDomElement element = node.toElement();
663 
664  QString name = node.namedItem( QStringLiteral( "layername" ) ).toElement().text();
665  if ( !name.isNull() )
666  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
667 
668  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
669  {
670  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, vLayerList );
671  continue;
672  }
673  else
674  {
675  if ( !addLayer( element, brokenNodes, vLayerList ) )
676  {
677  returnStatus = false;
678  }
679  }
680  emit layerLoaded( i + 1, nl.count() );
681  i++;
682  }
683 
684  // Update field map of layers with joins and create join caches if necessary
685  // Needs to be done here once all dependent layers are loaded
686  QList< QPair< QgsVectorLayer*, QDomElement > >::iterator vIt = vLayerList.begin();
687  for ( ; vIt != vLayerList.end(); ++vIt )
688  {
689  processLayerJoins( vIt->first );
690  }
691 
692  QSet<QgsVectorLayer *> notified;
693  for ( vIt = vLayerList.begin(); vIt != vLayerList.end(); ++vIt )
694  {
695  if ( notified.contains( vIt->first ) )
696  continue;
697 
698  notified << vIt->first;
699  emit readMapLayer( vIt->first, vIt->second );
700  }
701 
702  return returnStatus;
703 }
704 
705 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList )
706 {
707  QString type = layerElem.attribute( QStringLiteral( "type" ) );
708  QgsDebugMsg( "Layer type is " + type );
709  QgsMapLayer *mapLayer = nullptr;
710 
711  if ( type == QLatin1String( "vector" ) )
712  {
713  mapLayer = new QgsVectorLayer;
714  }
715  else if ( type == QLatin1String( "raster" ) )
716  {
717  mapLayer = new QgsRasterLayer;
718  }
719  else if ( type == QLatin1String( "plugin" ) )
720  {
721  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
722  mapLayer = QgsApplication::pluginLayerRegistry()->createLayer( typeName );
723  }
724 
725  if ( !mapLayer )
726  {
727  QgsDebugMsg( "Unable to create layer" );
728 
729  return false;
730  }
731 
732  Q_CHECK_PTR( mapLayer );
733 
734  // have the layer restore state that is stored in Dom node
735  if ( mapLayer->readLayerXml( layerElem ) && mapLayer->isValid() )
736  {
737  // postpone readMapLayer signal for vector layers with joins
738  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
739  if ( !vLayer || vLayer->vectorJoins().isEmpty() )
740  emit readMapLayer( mapLayer, layerElem );
741  else
742  vectorLayerList.push_back( qMakePair( vLayer, layerElem ) );
743 
744  QList<QgsMapLayer *> myLayers;
745  myLayers << mapLayer;
746  addMapLayers( myLayers );
747 
748  return true;
749  }
750  else
751  {
752  delete mapLayer;
753 
754  QgsDebugMsg( "Unable to load " + type + " layer" );
755  brokenNodes.push_back( layerElem );
756  return false;
757  }
758 }
759 
760 bool QgsProject::read( const QString& filename )
761 {
762  mFile.setFileName( filename );
763 
764  return read();
765 }
766 
768 {
769  clearError();
770 
771  QScopedPointer<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
772 
773  if ( !mFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
774  {
775  mFile.close();
776 
777  setError( tr( "Unable to open %1" ).arg( mFile.fileName() ) );
778 
779  return false;
780  }
781 
782  // location of problem associated with errorMsg
783  int line, column;
784  QString errorMsg;
785 
786  if ( !doc->setContent( &mFile, &errorMsg, &line, &column ) )
787  {
788  // want to make this class as GUI independent as possible; so commented out
789 #if 0
790  QMessageBox::critical( 0, tr( "Project File Read Error" ),
791  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
792 #endif
793 
794  QString errorString = tr( "Project file read error: %1 at line %2 column %3" )
795  .arg( errorMsg ).arg( line ).arg( column );
796 
797  QgsDebugMsg( errorString );
798 
799  mFile.close();
800 
801  setError( tr( "%1 for file %2" ).arg( errorString, mFile.fileName() ) );
802 
803  return false;
804  }
805 
806  mFile.close();
807 
808 
809  QgsDebugMsg( "Opened document " + mFile.fileName() );
810  QgsDebugMsg( "Project title: " + mTitle );
811 
812  // get project version string, if any
813  QgsProjectVersion fileVersion = getVersion( *doc );
814  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
815 
816  if ( thisVersion > fileVersion )
817  {
818  QgsLogger::warning( "Loading a file that was saved with an older "
819  "version of qgis (saved in " + fileVersion.text() +
820  ", loaded in " + Qgis::QGIS_VERSION +
821  "). Problems may occur." );
822 
823  QgsProjectFileTransform projectFile( *doc, fileVersion );
824 
826  emit oldProjectVersionWarning( fileVersion.text() );
827  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
828 
829  projectFile.updateRevision( thisVersion );
830  }
831 
832  // start new project, just keep the file name
833  QString fileName = mFile.fileName();
834  clear();
835  mFile.setFileName( fileName );
836 
837  // now get any properties
838  _getProperties( *doc, mProperties );
839 
840  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
841 
842  dump_( mProperties );
843 
844  // now get project title
845  _getTitle( *doc, mTitle );
846 
847  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
848  if ( nl.count() )
849  {
850  QDomElement transactionElement = nl.at( 0 ).toElement();
851  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
852  mAutoTransaction = true;
853  }
854 
855  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
856  if ( nl.count() )
857  {
858  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
859  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
860  mEvaluateDefaultValues = true;
861  }
862 
863  // read the layer tree from project file
864 
865  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
866 
867  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
868  if ( !layerTreeElem.isNull() )
869  {
870  mRootGroup->readChildrenFromXml( layerTreeElem );
871  }
872  else
873  {
874  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
875  }
876 
877  QgsDebugMsg( "Loaded layer tree:\n " + mRootGroup->dump() );
878 
879  mLayerTreeRegistryBridge->setEnabled( false );
880 
881  // get the map layers
882  QList<QDomNode> brokenNodes;
883  bool clean = _getMapLayers( *doc, brokenNodes );
884 
885  // review the integrity of the retrieved map layers
886  if ( !clean )
887  {
888  QgsDebugMsg( "Unable to get map layers from project file." );
889 
890  if ( !brokenNodes.isEmpty() )
891  {
892  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
893  }
894 
895  // we let a custom handler decide what to do with missing layers
896  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
897  mBadLayerHandler->handleBadLayers( brokenNodes );
898  }
899 
900  mLayerTreeRegistryBridge->setEnabled( true );
901 
902  // load embedded groups and layers
903  loadEmbeddedNodes( mRootGroup );
904 
905  // make sure the are just valid layers
907 
908  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
909 
910  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
912  mMapThemeCollection->readXml( *doc );
913 
914  // reassign change dependencies now that all layers are loaded
915  QMap<QString, QgsMapLayer*> existingMaps = mapLayers();
916  for ( QMap<QString, QgsMapLayer*>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
917  {
918  it.value()->setDependencies( it.value()->dependencies() );
919  }
920 
921  mSnappingConfig.readProject( *doc );
922  emit snappingConfigChanged();
923 
924  //add variables defined in project file
925  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
926  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
927 
928  mCustomVariables.clear();
929  if ( variableNames.length() == variableValues.length() )
930  {
931  for ( int i = 0; i < variableNames.length(); ++i )
932  {
933  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
934  }
935  }
936  else
937  {
938  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
939  }
940  emit customVariablesChanged();
941 
942  // read the project: used by map canvas and legend
943  emit readProject( *doc );
944 
945  // if all went well, we're allegedly in pristine state
946  if ( clean )
947  setDirty( false );
948 
950 
951  return true;
952 }
953 
954 
955 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group )
956 {
957  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
958  {
959  if ( QgsLayerTree::isGroup( child ) )
960  {
961  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
962  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
963  {
964  // make sure to convert the path from relative to absolute
965  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
966  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
967 
968  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList() );
969  if ( newGroup )
970  {
971  QList<QgsLayerTreeNode*> clonedChildren;
972  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
973  clonedChildren << newGroupChild->clone();
974  delete newGroup;
975 
976  childGroup->insertChildNodes( 0, clonedChildren );
977  }
978  }
979  else
980  {
981  loadEmbeddedNodes( childGroup );
982  }
983  }
984  else if ( QgsLayerTree::isLayer( child ) )
985  {
986  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
987  {
988  QList<QDomNode> brokenNodes;
989  QList< QPair< QgsVectorLayer*, QDomElement > > vectorLayerList;
990  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes, vectorLayerList );
991  }
992  }
993 
994  }
995 }
996 
997 QVariantMap QgsProject::customVariables() const
998 {
999  return mCustomVariables;
1000 }
1001 
1002 void QgsProject::setCustomVariables( const QVariantMap& variables )
1003 {
1004  if ( variables == mCustomVariables )
1005  return;
1006 
1007  //write variable to project
1008  QStringList variableNames;
1009  QStringList variableValues;
1010 
1011  QVariantMap::const_iterator it = variables.constBegin();
1012  for ( ; it != variables.constEnd(); ++it )
1013  {
1014  variableNames << it.key();
1015  variableValues << it.value().toString();
1016  }
1017 
1018  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1019  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1020 
1021  mCustomVariables = variables;
1022 
1023  emit customVariablesChanged();
1024 }
1025 
1026 QList<QgsVectorLayer*> QgsProject::avoidIntersectionsLayers() const
1027 {
1028  QList<QgsVectorLayer*> layers;
1029  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1030  Q_FOREACH ( const QString& layerId, layerIds )
1031  {
1032  if ( QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( mapLayer( layerId ) ) )
1033  layers << vlayer;
1034  }
1035  return layers;
1036 }
1037 
1038 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer*>& layers )
1039 {
1040  QStringList list;
1041  Q_FOREACH ( QgsVectorLayer* layer, layers )
1042  list << layer->id();
1043  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1045 }
1046 
1048 {
1049  QgsExpressionContext context;
1050 
1053 
1054  return context;
1055 }
1056 
1057 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer*>& layers )
1058 {
1059  QMap<QString, QgsMapLayer*> existingMaps = mapLayers();
1060 
1061  bool tgChanged = false;
1062 
1063  Q_FOREACH ( QgsMapLayer* layer, layers )
1064  {
1065  QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( layer );
1066  if ( vlayer )
1067  {
1068  if ( autoTransaction() )
1069  {
1070  if ( QgsTransaction::supportsTransaction( vlayer ) )
1071  {
1072  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1073  QString key = vlayer->providerType();
1074 
1075  QgsTransactionGroup* tg = mTransactionGroups.value( qMakePair( key, connString ) );
1076 
1077  if ( !tg )
1078  {
1079  tg = new QgsTransactionGroup();
1080  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1081  tgChanged = true;
1082  }
1083  tg->addLayer( vlayer );
1084  }
1085  }
1087  }
1088 
1089  if ( tgChanged )
1090  emit transactionGroupsChanged();
1091 
1092  connect( layer, SIGNAL( configChanged() ), this, SLOT( setDirty() ) );
1093 
1094  // check if we have to update connections for layers with dependencies
1095  for ( QMap<QString, QgsMapLayer*>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1096  {
1097  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1098  if ( deps.contains( layer->id() ) )
1099  {
1100  // reconnect to change signals
1101  it.value()->setDependencies( deps );
1102  }
1103  }
1104  }
1105 
1106  if ( mSnappingConfig.addLayers( layers ) )
1107  emit snappingConfigChanged();
1108 }
1109 
1110 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer*>& layers )
1111 {
1112  if ( mSnappingConfig.removeLayers( layers ) )
1113  emit snappingConfigChanged();
1114 }
1115 
1116 void QgsProject::cleanTransactionGroups( bool force )
1117 {
1118  bool changed = false;
1119  for ( QMap< QPair< QString, QString>, QgsTransactionGroup*>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1120  {
1121  if ( tg.value()->isEmpty() || force )
1122  {
1123  delete tg.value();
1124  tg = mTransactionGroups.erase( tg );
1125  changed = true;
1126  }
1127  else
1128  {
1129  ++tg;
1130  }
1131  }
1132  if ( changed )
1133  emit transactionGroupsChanged();
1134 }
1135 
1136 bool QgsProject::readLayer( const QDomNode& layerNode )
1137 {
1138  QList<QDomNode> brokenNodes;
1139  QList< QPair< QgsVectorLayer*, QDomElement > > vectorLayerList;
1140  if ( addLayer( layerNode.toElement(), brokenNodes, vectorLayerList ) )
1141  {
1142  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1143  // added layer for joins
1144  QVector<QgsVectorLayer*> vectorLayers = layers<QgsVectorLayer*>();
1145  Q_FOREACH ( QgsVectorLayer* layer, vectorLayers )
1146  {
1147  processLayerJoins( layer );
1148  }
1149 
1150  if ( !vectorLayerList.isEmpty() )
1151  {
1152  emit readMapLayer( vectorLayerList.at( 0 ).first, vectorLayerList.at( 0 ).second );
1153  }
1154 
1155  return true;
1156  }
1157  return false;
1158 }
1159 
1160 bool QgsProject::write( const QString& filename )
1161 {
1162  mFile.setFileName( filename );
1163 
1164  return write();
1165 }
1166 
1168 {
1169  clearError();
1170 
1171  // if we have problems creating or otherwise writing to the project file,
1172  // let's find out up front before we go through all the hand-waving
1173  // necessary to create all the Dom objects
1174  QFileInfo myFileInfo( mFile );
1175  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1176  {
1177  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1178  .arg( mFile.fileName() ) );
1179  return false;
1180  }
1181 
1182  QDomImplementation DomImplementation;
1183  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1184 
1185  QDomDocumentType documentType =
1186  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1187  QStringLiteral( "SYSTEM" ) );
1188  QScopedPointer<QDomDocument> doc( new QDomDocument( documentType ) );
1189 
1190  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1191  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1192  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1193 
1194  doc->appendChild( qgisNode );
1195 
1196  // title
1197  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1198  qgisNode.appendChild( titleNode );
1199 
1200  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1201  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? "1" : "0" );
1202  qgisNode.appendChild( transactionNode );
1203 
1204  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1205  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? "1" : "0" );
1206  qgisNode.appendChild( evaluateDefaultValuesNode );
1207 
1208  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1209  titleNode.appendChild( titleText );
1210 
1211  // write layer tree - make sure it is without embedded subgroups
1212  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1214  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ) ); // convert absolute paths to relative paths if required
1215  clonedRoot->writeXml( qgisNode );
1216  delete clonedRoot;
1217 
1218  mSnappingConfig.writeProject( *doc );
1219 
1220  // let map canvas and legend write their information
1221  emit writeProject( *doc );
1222 
1223  // within top level node save list of layers
1224  const QMap<QString, QgsMapLayer*> &layers = mapLayers();
1225 
1226  // Iterate over layers in zOrder
1227  // Call writeXml() on each
1228  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1229 
1230  QMap<QString, QgsMapLayer*>::ConstIterator li = layers.constBegin();
1231  while ( li != layers.end() )
1232  {
1233  QgsMapLayer *ml = li.value();
1234 
1235  if ( ml )
1236  {
1237  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1238  if ( emIt == mEmbeddedLayers.constEnd() )
1239  {
1240  // general layer metadata
1241  QDomElement maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1242 
1243  ml->writeLayerXml( maplayerElem, *doc );
1244 
1245  emit writeMapLayer( ml, maplayerElem, *doc );
1246 
1247  projectLayersNode.appendChild( maplayerElem );
1248  }
1249  else
1250  {
1251  // layer defined in an external project file
1252  // only save embedded layer if not managed by a legend group
1253  if ( emIt.value().second )
1254  {
1255  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1256  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
1257  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
1258  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
1259  projectLayersNode.appendChild( mapLayerElem );
1260  }
1261  }
1262  }
1263  li++;
1264  }
1265 
1266  qgisNode.appendChild( projectLayersNode );
1267 
1268  // now add the optional extra properties
1269 
1270  dump_( mProperties );
1271 
1272  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
1273 
1274  if ( !mProperties.isEmpty() ) // only worry about properties if we
1275  // actually have any properties
1276  {
1277  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
1278  }
1279 
1280  mMapThemeCollection->writeXml( *doc );
1281 
1282  // now wrap it up and ship it to the project file
1283  doc->normalize(); // XXX I'm not entirely sure what this does
1284 
1285  // Create backup file
1286  if ( QFile::exists( fileName() ) )
1287  {
1288  QFile backupFile( fileName() + '~' );
1289  bool ok = true;
1290  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1291  ok &= mFile.open( QIODevice::ReadOnly );
1292 
1293  QByteArray ba;
1294  while ( ok && !mFile.atEnd() )
1295  {
1296  ba = mFile.read( 10240 );
1297  ok &= backupFile.write( ba ) == ba.size();
1298  }
1299 
1300  mFile.close();
1301  backupFile.close();
1302 
1303  if ( !ok )
1304  {
1305  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1306  return false;
1307  }
1308 
1309  QFileInfo fi( fileName() );
1310  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1311  utime( backupFile.fileName().toUtf8().constData(), &tb );
1312  }
1313 
1314  if ( !mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1315  {
1316  mFile.close(); // even though we got an error, let's make
1317  // sure it's closed anyway
1318 
1319  setError( tr( "Unable to save to file %1" ).arg( mFile.fileName() ) );
1320  return false;
1321  }
1322 
1323  QTemporaryFile tempFile;
1324  bool ok = tempFile.open();
1325  if ( ok )
1326  {
1327  QTextStream projectFileStream( &tempFile );
1328  doc->save( projectFileStream, 2 ); // save as utf-8
1329  ok &= projectFileStream.pos() > -1;
1330 
1331  ok &= tempFile.seek( 0 );
1332 
1333  QByteArray ba;
1334  while ( ok && !tempFile.atEnd() )
1335  {
1336  ba = tempFile.read( 10240 );
1337  ok &= mFile.write( ba ) == ba.size();
1338  }
1339 
1340  ok &= mFile.error() == QFile::NoError;
1341 
1342  mFile.close();
1343  }
1344 
1345  tempFile.close();
1346 
1347  if ( !ok )
1348  {
1349  setError( tr( "Unable to save to file %1. Your project "
1350  "may be corrupted on disk. Try clearing some space on the volume and "
1351  "check file permissions before pressing save again." )
1352  .arg( mFile.fileName() ) );
1353  return false;
1354  }
1355 
1356  setDirty( false ); // reset to pristine state
1357 
1358  emit projectSaved();
1359 
1360  return true;
1361 }
1362 
1363 bool QgsProject::writeEntry( const QString& scope, QString const& key, bool value )
1364 {
1365  setDirty( true );
1366 
1367  return addKey_( scope, key, &mProperties, value );
1368 }
1369 
1370 bool QgsProject::writeEntry( const QString& scope, const QString& key, double value )
1371 {
1372  setDirty( true );
1373 
1374  return addKey_( scope, key, &mProperties, value );
1375 }
1376 
1377 bool QgsProject::writeEntry( const QString& scope, QString const& key, int value )
1378 {
1379  setDirty( true );
1380 
1381  return addKey_( scope, key, &mProperties, value );
1382 }
1383 
1384 bool QgsProject::writeEntry( const QString& scope, const QString& key, const QString& value )
1385 {
1386  setDirty( true );
1387 
1388  return addKey_( scope, key, &mProperties, value );
1389 }
1390 
1391 bool QgsProject::writeEntry( const QString& scope, const QString& key, const QStringList& value )
1392 {
1393  setDirty( true );
1394 
1395  return addKey_( scope, key, &mProperties, value );
1396 }
1397 
1398 QStringList QgsProject::readListEntry( const QString& scope,
1399  const QString &key,
1400  const QStringList& def,
1401  bool* ok ) const
1402 {
1403  QgsProjectProperty* property = findKey_( scope, key, mProperties );
1404 
1405  QVariant value;
1406 
1407  if ( property )
1408  {
1409  value = property->value();
1410 
1411  bool valid = QVariant::StringList == value.type();
1412  if ( ok )
1413  *ok = valid;
1414 
1415  if ( valid )
1416  {
1417  return value.toStringList();
1418  }
1419  }
1420 
1421  return def;
1422 }
1423 
1424 
1425 QString QgsProject::readEntry( const QString& scope,
1426  const QString& key,
1427  const QString& def,
1428  bool* ok ) const
1429 {
1430  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1431 
1432  QVariant value;
1433 
1434  if ( property )
1435  {
1436  value = property->value();
1437 
1438  bool valid = value.canConvert( QVariant::String );
1439  if ( ok )
1440  *ok = valid;
1441 
1442  if ( valid )
1443  return value.toString();
1444  }
1445 
1446  return def;
1447 }
1448 
1449 int QgsProject::readNumEntry( const QString& scope, const QString &key, int def,
1450  bool* ok ) const
1451 {
1452  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1453 
1454  QVariant value;
1455 
1456  if ( property )
1457  {
1458  value = property->value();
1459  }
1460 
1461  bool valid = value.canConvert( QVariant::String );
1462 
1463  if ( ok )
1464  {
1465  *ok = valid;
1466  }
1467 
1468  if ( valid )
1469  {
1470  return value.toInt();
1471  }
1472 
1473  return def;
1474 }
1475 
1476 double QgsProject::readDoubleEntry( const QString& scope, const QString& key,
1477  double def,
1478  bool* ok ) const
1479 {
1480  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1481  if ( property )
1482  {
1483  QVariant value = property->value();
1484 
1485  bool valid = value.canConvert( QVariant::Double );
1486  if ( ok )
1487  *ok = valid;
1488 
1489  if ( valid )
1490  return value.toDouble();
1491  }
1492 
1493  return def;
1494 }
1495 
1496 bool QgsProject::readBoolEntry( const QString& scope, const QString &key, bool def,
1497  bool* ok ) const
1498 {
1499  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1500 
1501  if ( property )
1502  {
1503  QVariant value = property->value();
1504 
1505  bool valid = value.canConvert( QVariant::Bool );
1506  if ( ok )
1507  *ok = valid;
1508 
1509  if ( valid )
1510  return value.toBool();
1511  }
1512 
1513  return def;
1514 }
1515 
1516 
1517 bool QgsProject::removeEntry( const QString& scope, const QString& key )
1518 {
1519  removeKey_( scope, key, mProperties );
1520 
1521  setDirty( true );
1522 
1523  return !findKey_( scope, key, mProperties );
1524 }
1525 
1526 
1527 QStringList QgsProject::entryList( const QString& scope, const QString& key ) const
1528 {
1529  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1530 
1531  QStringList entries;
1532 
1533  if ( foundProperty )
1534  {
1535  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey*>( foundProperty );
1536 
1537  if ( propertyKey )
1538  { propertyKey->entryList( entries ); }
1539  }
1540 
1541  return entries;
1542 }
1543 
1544 QStringList QgsProject::subkeyList( const QString& scope, const QString& key ) const
1545 {
1546  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1547 
1548  QStringList entries;
1549 
1550  if ( foundProperty )
1551  {
1552  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey*>( foundProperty );
1553 
1554  if ( propertyKey )
1555  { propertyKey->subkeyList( entries ); }
1556  }
1557 
1558  return entries;
1559 }
1560 
1562 {
1563  dump_( mProperties );
1564 }
1565 
1566 QString QgsProject::readPath( QString src ) const
1567 {
1568  if ( readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false ) )
1569  {
1570  return src;
1571  }
1572 
1573  // if this is a VSIFILE, remove the VSI prefix and append to final result
1574  QString vsiPrefix = qgsVsiPrefix( src );
1575  if ( ! vsiPrefix.isEmpty() )
1576  {
1577  // unfortunately qgsVsiPrefix returns prefix also for files like "/x/y/z.gz"
1578  // so we need to check if we really have the prefix
1579  if ( src.startsWith( QLatin1String( "/vsi" ), Qt::CaseInsensitive ) )
1580  src.remove( 0, vsiPrefix.size() );
1581  else
1582  vsiPrefix.clear();
1583  }
1584 
1585  // relative path should always start with ./ or ../
1586  if ( !src.startsWith( QLatin1String( "./" ) ) && !src.startsWith( QLatin1String( "../" ) ) )
1587  {
1588 #if defined(Q_OS_WIN)
1589  if ( src.startsWith( "\\\\" ) ||
1590  src.startsWith( "//" ) ||
1591  ( src[0].isLetter() && src[1] == ':' ) )
1592  {
1593  // UNC or absolute path
1594  return vsiPrefix + src;
1595  }
1596 #else
1597  if ( src[0] == '/' )
1598  {
1599  // absolute path
1600  return vsiPrefix + src;
1601  }
1602 #endif
1603 
1604  // so this one isn't absolute, but also doesn't start // with ./ or ../.
1605  // That means that it was saved with an earlier version of "relative path support",
1606  // where the source file had to exist and only the project directory was stripped
1607  // from the filename.
1608  QString home = homePath();
1609  if ( home.isNull() )
1610  return vsiPrefix + src;
1611 
1612  QFileInfo fi( home + '/' + src );
1613 
1614  if ( !fi.exists() )
1615  {
1616  return vsiPrefix + src;
1617  }
1618  else
1619  {
1620  return vsiPrefix + fi.canonicalFilePath();
1621  }
1622  }
1623 
1624  QString srcPath = src;
1625  QString projPath = fileName();
1626 
1627  if ( projPath.isEmpty() )
1628  {
1629  return vsiPrefix + src;
1630  }
1631 
1632 #if defined(Q_OS_WIN)
1633  srcPath.replace( '\\', '/' );
1634  projPath.replace( '\\', '/' );
1635 
1636  bool uncPath = projPath.startsWith( "//" );
1637 #endif
1638 
1639  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1640  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1641 
1642 #if defined(Q_OS_WIN)
1643  if ( uncPath )
1644  {
1645  projElems.insert( 0, "" );
1646  projElems.insert( 0, "" );
1647  }
1648 #endif
1649 
1650  // remove project file element
1651  projElems.removeLast();
1652 
1653  // append source path elements
1654  projElems << srcElems;
1655  projElems.removeAll( QStringLiteral( "." ) );
1656 
1657  // resolve ..
1658  int pos;
1659  while (( pos = projElems.indexOf( QStringLiteral( ".." ) ) ) > 0 )
1660  {
1661  // remove preceding element and ..
1662  projElems.removeAt( pos - 1 );
1663  projElems.removeAt( pos - 1 );
1664  }
1665 
1666 #if !defined(Q_OS_WIN)
1667  // make path absolute
1668  projElems.prepend( QLatin1String( "" ) );
1669 #endif
1670 
1671  return vsiPrefix + projElems.join( QStringLiteral( "/" ) );
1672 }
1673 
1674 QString QgsProject::writePath( const QString& src, const QString& relativeBasePath ) const
1675 {
1676  if ( readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false ) || src.isEmpty() )
1677  {
1678  return src;
1679  }
1680 
1681  QFileInfo srcFileInfo( src );
1682  QFileInfo projFileInfo( fileName() );
1683  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
1684  QString projPath = projFileInfo.canonicalFilePath();
1685 
1686  if ( !relativeBasePath.isNull() )
1687  {
1688  projPath = relativeBasePath;
1689  }
1690 
1691  if ( projPath.isEmpty() )
1692  {
1693  return src;
1694  }
1695 
1696  // if this is a VSIFILE, remove the VSI prefix and append to final result
1697  QString vsiPrefix = qgsVsiPrefix( src );
1698  if ( ! vsiPrefix.isEmpty() )
1699  {
1700  srcPath.remove( 0, vsiPrefix.size() );
1701  }
1702 
1703 #if defined( Q_OS_WIN )
1704  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1705 
1706  srcPath.replace( '\\', '/' );
1707 
1708  if ( srcPath.startsWith( "//" ) )
1709  {
1710  // keep UNC prefix
1711  srcPath = "\\\\" + srcPath.mid( 2 );
1712  }
1713 
1714  projPath.replace( '\\', '/' );
1715  if ( projPath.startsWith( "//" ) )
1716  {
1717  // keep UNC prefix
1718  projPath = "\\\\" + projPath.mid( 2 );
1719  }
1720 #else
1721  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1722 #endif
1723 
1724  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1725  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1726 
1727  // remove project file element
1728  projElems.removeLast();
1729 
1730  projElems.removeAll( QStringLiteral( "." ) );
1731  srcElems.removeAll( QStringLiteral( "." ) );
1732 
1733  // remove common part
1734  int n = 0;
1735  while ( !srcElems.isEmpty() &&
1736  !projElems.isEmpty() &&
1737  srcElems[0].compare( projElems[0], cs ) == 0 )
1738  {
1739  srcElems.removeFirst();
1740  projElems.removeFirst();
1741  n++;
1742  }
1743 
1744  if ( n == 0 )
1745  {
1746  // no common parts; might not even by a file
1747  return src;
1748  }
1749 
1750  if ( !projElems.isEmpty() )
1751  {
1752  // go up to the common directory
1753  for ( int i = 0; i < projElems.size(); i++ )
1754  {
1755  srcElems.insert( 0, QStringLiteral( ".." ) );
1756  }
1757  }
1758  else
1759  {
1760  // let it start with . nevertheless,
1761  // so relative path always start with either ./ or ../
1762  srcElems.insert( 0, QStringLiteral( "." ) );
1763  }
1764 
1765  return vsiPrefix + srcElems.join( QStringLiteral( "/" ) );
1766 }
1767 
1768 void QgsProject::setError( const QString& errorMessage )
1769 {
1770  mErrorMessage = errorMessage;
1771 }
1772 
1773 QString QgsProject::error() const
1774 {
1775  return mErrorMessage;
1776 }
1777 
1778 void QgsProject::clearError()
1779 {
1780  setError( QString() );
1781 }
1782 
1784 {
1785  delete mBadLayerHandler;
1786  mBadLayerHandler = handler;
1787 }
1788 
1789 QString QgsProject::layerIsEmbedded( const QString &id ) const
1790 {
1791  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1792  if ( it == mEmbeddedLayers.constEnd() )
1793  {
1794  return QString();
1795  }
1796  return it.value().first;
1797 }
1798 
1799 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1800  QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList, bool saveFlag )
1801 {
1802  QgsDebugCall;
1803 
1804  static QString sPrevProjectFilePath;
1805  static QDateTime sPrevProjectFileTimestamp;
1806  static QDomDocument sProjectDocument;
1807 
1808  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1809 
1810  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
1811  {
1812  sPrevProjectFilePath.clear();
1813 
1814  QFile projectFile( projectFilePath );
1815  if ( !projectFile.open( QIODevice::ReadOnly ) )
1816  {
1817  return false;
1818  }
1819 
1820  if ( !sProjectDocument.setContent( &projectFile ) )
1821  {
1822  return false;
1823  }
1824 
1825  sPrevProjectFilePath = projectFilePath;
1826  sPrevProjectFileTimestamp = projectFileTimestamp;
1827  }
1828 
1829  // does project store paths absolute or relative?
1830  bool useAbsolutePaths = true;
1831 
1832  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1833  if ( !propertiesElem.isNull() )
1834  {
1835  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
1836  if ( !absElem.isNull() )
1837  {
1838  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
1839  }
1840  }
1841 
1842  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
1843  if ( projectLayersElem.isNull() )
1844  {
1845  return false;
1846  }
1847 
1848  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
1849  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1850  {
1851  // get layer id
1852  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1853  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
1854  if ( id == layerId )
1855  {
1856  // layer can be embedded only once
1857  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1858  {
1859  return false;
1860  }
1861 
1862  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1863 
1864  // change datasource path from relative to absolute if necessary
1865  // see also QgsMapLayer::readLayerXML
1866  if ( !useAbsolutePaths )
1867  {
1868  QString provider( mapLayerElem.firstChildElement( QStringLiteral( "provider" ) ).text() );
1869  QDomElement dsElem( mapLayerElem.firstChildElement( QStringLiteral( "datasource" ) ) );
1870  QString datasource( dsElem.text() );
1871  if ( provider == QLatin1String( "spatialite" ) )
1872  {
1873  QgsDataSourceUri uri( datasource );
1874  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + uri.database() );
1875  if ( absoluteDs.exists() )
1876  {
1877  uri.setDatabase( absoluteDs.absoluteFilePath() );
1878  datasource = uri.uri();
1879  }
1880  }
1881  else if ( provider == QLatin1String( "ogr" ) )
1882  {
1883  QStringList theURIParts( datasource.split( '|' ) );
1884  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1885  if ( absoluteDs.exists() )
1886  {
1887  theURIParts[0] = absoluteDs.absoluteFilePath();
1888  datasource = theURIParts.join( QStringLiteral( "|" ) );
1889  }
1890  }
1891  else if ( provider == QLatin1String( "gpx" ) )
1892  {
1893  QStringList theURIParts( datasource.split( '?' ) );
1894  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1895  if ( absoluteDs.exists() )
1896  {
1897  theURIParts[0] = absoluteDs.absoluteFilePath();
1898  datasource = theURIParts.join( QStringLiteral( "?" ) );
1899  }
1900  }
1901  else if ( provider == QLatin1String( "delimitedtext" ) )
1902  {
1903  QUrl urlSource( QUrl::fromEncoded( datasource.toLatin1() ) );
1904 
1905  if ( !datasource.startsWith( QLatin1String( "file:" ) ) )
1906  {
1907  QUrl file( QUrl::fromLocalFile( datasource.left( datasource.indexOf( '?' ) ) ) );
1908  urlSource.setScheme( QStringLiteral( "file" ) );
1909  urlSource.setPath( file.path() );
1910  }
1911 
1912  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + urlSource.toLocalFile() );
1913  if ( absoluteDs.exists() )
1914  {
1915  QUrl urlDest = QUrl::fromLocalFile( absoluteDs.absoluteFilePath() );
1916  urlDest.setQueryItems( urlSource.queryItems() );
1917  datasource = QString::fromAscii( urlDest.toEncoded() );
1918  }
1919  }
1920  else
1921  {
1922  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + datasource );
1923  if ( absoluteDs.exists() )
1924  {
1925  datasource = absoluteDs.absoluteFilePath();
1926  }
1927  }
1928 
1929  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1930  dsElem.appendChild( sProjectDocument.createTextNode( datasource ) );
1931  }
1932 
1933  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1934  {
1935  return true;
1936  }
1937  else
1938  {
1939  mEmbeddedLayers.remove( layerId );
1940  return false;
1941  }
1942  }
1943  }
1944 
1945  return false;
1946 }
1947 
1948 
1949 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1950 {
1951  // open project file, get layer ids in group, add the layers
1952  QFile projectFile( projectFilePath );
1953  if ( !projectFile.open( QIODevice::ReadOnly ) )
1954  {
1955  return nullptr;
1956  }
1957 
1958  QDomDocument projectDocument;
1959  if ( !projectDocument.setContent( &projectFile ) )
1960  {
1961  return nullptr;
1962  }
1963 
1964  // store identify disabled layers of the embedded project
1965  QSet<QString> embeddedIdentifyDisabledLayers;
1966  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) ).firstChildElement( QStringLiteral( "Identify" ) ).firstChildElement( QStringLiteral( "disabledLayers" ) );
1967  if ( !disabledLayersElem.isNull() )
1968  {
1969  QDomNodeList valueList = disabledLayersElem.elementsByTagName( QStringLiteral( "value" ) );
1970  for ( int i = 0; i < valueList.size(); ++i )
1971  {
1972  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1973  }
1974  }
1975 
1977 
1978  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1979  if ( !layerTreeElem.isNull() )
1980  {
1981  root->readChildrenFromXml( layerTreeElem );
1982  }
1983  else
1984  {
1985  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1986  }
1987 
1988  QgsLayerTreeGroup *group = root->findGroup( groupName );
1989  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
1990  {
1991  // embedded groups cannot be embedded again
1992  delete root;
1993  return nullptr;
1994  }
1995 
1996  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1997  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1998  delete root;
1999  root = nullptr;
2000 
2001  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2002  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2003 
2004  // set "embedded" to all children + load embedded layers
2005  mLayerTreeRegistryBridge->setEnabled( false );
2006  initializeEmbeddedSubtree( projectFilePath, newGroup );
2007  mLayerTreeRegistryBridge->setEnabled( true );
2008 
2009  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
2010 
2011  // consider the layers might be identify disabled in its project
2012  Q_FOREACH ( const QString& layerId, newGroup->findLayerIds() )
2013  {
2014  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
2015  {
2016  thisProjectIdentifyDisabledLayers.append( layerId );
2017  }
2018 
2019  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2020  if ( layer )
2021  {
2022  layer->setItemVisibilityChecked( invisibleLayers.contains( layerId ) );
2023  }
2024  }
2025 
2026  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
2027 
2028  return newGroup;
2029 }
2030 
2031 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group )
2032 {
2033  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
2034  {
2035  // all nodes in the subtree will have "embedded" custom property set
2036  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2037 
2038  if ( QgsLayerTree::isGroup( child ) )
2039  {
2040  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
2041  }
2042  else if ( QgsLayerTree::isLayer( child ) )
2043  {
2044  // load the layer into our project
2045  QList<QDomNode> brokenNodes;
2046  QList< QPair< QgsVectorLayer*, QDomElement > > vectorLayerList;
2047  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, vectorLayerList, false );
2048  }
2049  }
2050 }
2051 
2053 {
2054  return mEvaluateDefaultValues;
2055 }
2056 
2058 {
2059  Q_FOREACH ( QgsMapLayer* layer, mapLayers().values() )
2060  {
2061  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( layer );
2062  if ( vl )
2063  {
2065  }
2066  }
2067 
2068  mEvaluateDefaultValues = evaluateDefaultValues;
2069 }
2070 
2072 {
2073  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2075 }
2076 
2078 {
2079  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2080 }
2081 
2083 {
2084  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2085  if ( !distanceUnitString.isEmpty() )
2086  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2087 
2088  //fallback to QGIS default measurement unit
2089  QSettings s;
2090  bool ok = false;
2091  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2092  return ok ? type : QgsUnitTypes::DistanceMeters;
2093 }
2094 
2096 {
2097  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2098 }
2099 
2101 {
2102  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2103  if ( !areaUnitString.isEmpty() )
2104  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2105 
2106  //fallback to QGIS default area unit
2107  QSettings s;
2108  bool ok = false;
2109  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2110  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2111 }
2112 
2114 {
2115  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2116 }
2117 
2118 QString QgsProject::homePath() const
2119 {
2120  QFileInfo pfi( fileName() );
2121  if ( !pfi.exists() )
2122  return QString::null;
2123 
2124  return pfi.canonicalPath();
2125 }
2126 
2128 {
2129  return mRelationManager;
2130 }
2131 
2133 {
2134  return mRootGroup;
2135 }
2136 
2138 {
2139  return mMapThemeCollection.data();
2140 }
2141 
2142 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer*>& layers )
2143 {
2144  QStringList currentLayers = nonIdentifiableLayers();
2145 
2146  QStringList newLayers;
2147  Q_FOREACH ( QgsMapLayer* l, layers )
2148  {
2149  newLayers << l->id();
2150  }
2151 
2152  if ( newLayers == currentLayers )
2153  return;
2154 
2155  QStringList disabledLayerIds;
2156 
2157  Q_FOREACH ( QgsMapLayer* l, layers )
2158  {
2159  disabledLayerIds << l->id();
2160  }
2161 
2162  setNonIdentifiableLayers( disabledLayerIds );
2163 }
2164 
2165 void QgsProject::setNonIdentifiableLayers( const QStringList& layerIds )
2166 {
2167  writeEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ), layerIds );
2168 
2169  emit nonIdentifiableLayersChanged( layerIds );
2170 }
2171 
2172 QStringList QgsProject::nonIdentifiableLayers() const
2173 {
2174  return readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
2175 }
2176 
2178 {
2179  return mAutoTransaction;
2180 }
2181 
2183 {
2184  if ( autoTransaction != mAutoTransaction )
2185  {
2186  mAutoTransaction = autoTransaction;
2187 
2188  if ( autoTransaction )
2189  onMapLayersAdded( mapLayers().values() );
2190  else
2191  cleanTransactionGroups( true );
2192  }
2193 }
2194 
2195 QMap<QPair<QString, QString>, QgsTransactionGroup*> QgsProject::transactionGroups()
2196 {
2197  return mTransactionGroups;
2198 }
2199 
2200 
2201 //
2202 // QgsMapLayerRegistry methods
2203 //
2204 
2205 
2207 {
2208  return mMapLayers.size();
2209 }
2210 
2211 QgsMapLayer * QgsProject::mapLayer( const QString& theLayerId ) const
2212 {
2213  return mMapLayers.value( theLayerId );
2214 }
2215 
2216 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString& layerName ) const
2217 {
2218  QList<QgsMapLayer *> myResultList;
2219  Q_FOREACH ( QgsMapLayer* layer, mMapLayers )
2220  {
2221  if ( layer->name() == layerName )
2222  {
2223  myResultList << layer;
2224  }
2225  }
2226  return myResultList;
2227 }
2228 
2229 QList<QgsMapLayer *> QgsProject::addMapLayers(
2230  const QList<QgsMapLayer *>& theMapLayers,
2231  bool addToLegend,
2232  bool takeOwnership )
2233 {
2234  QList<QgsMapLayer *> myResultList;
2235  Q_FOREACH ( QgsMapLayer* myLayer, theMapLayers )
2236  {
2237  if ( !myLayer || !myLayer->isValid() )
2238  {
2239  QgsDebugMsg( "Cannot add invalid layers" );
2240  continue;
2241  }
2242  //check the layer is not already registered!
2243  if ( !mMapLayers.contains( myLayer->id() ) )
2244  {
2245  mMapLayers[myLayer->id()] = myLayer;
2246  myResultList << mMapLayers[myLayer->id()];
2247  if ( takeOwnership )
2248  {
2249  myLayer->setParent( this );
2250  }
2251  connect( myLayer, SIGNAL( destroyed( QObject* ) ), this, SLOT( onMapLayerDeleted( QObject* ) ) );
2252  emit layerWasAdded( myLayer );
2253  }
2254  }
2255  if ( !myResultList.isEmpty() )
2256  {
2257  emit layersAdded( myResultList );
2258 
2259  if ( addToLegend )
2260  emit legendLayersAdded( myResultList );
2261  }
2262  return myResultList;
2263 }
2264 
2265 QgsMapLayer *
2267  bool addToLegend,
2268  bool takeOwnership )
2269 {
2270  QList<QgsMapLayer *> addedLayers;
2271  addedLayers = addMapLayers( QList<QgsMapLayer*>() << theMapLayer, addToLegend, takeOwnership );
2272  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
2273 }
2274 
2275 void QgsProject::removeMapLayers( const QStringList& theLayerIds )
2276 {
2277  QList<QgsMapLayer*> layers;
2278  Q_FOREACH ( const QString &myId, theLayerIds )
2279  {
2280  layers << mMapLayers.value( myId );
2281  }
2282 
2283  removeMapLayers( layers );
2284 }
2285 
2286 void QgsProject::removeMapLayers( const QList<QgsMapLayer*>& layers )
2287 {
2288  if ( layers.isEmpty() )
2289  return;
2290 
2291  QStringList layerIds;
2292  QList<QgsMapLayer*> layerList;
2293 
2294  Q_FOREACH ( QgsMapLayer* layer, layers )
2295  {
2296  // check layer and the registry contains it
2297  if ( layer && mMapLayers.contains( layer->id() ) )
2298  {
2299  layerIds << layer->id();
2300  layerList << layer;
2301  }
2302  }
2303 
2304  if ( layerIds.isEmpty() )
2305  return;
2306 
2307  emit layersWillBeRemoved( layerIds );
2308  emit layersWillBeRemoved( layerList );
2309 
2310  Q_FOREACH ( QgsMapLayer* lyr, layerList )
2311  {
2312  QString myId( lyr->id() );
2313  emit layerWillBeRemoved( myId );
2314  emit layerWillBeRemoved( lyr );
2315  mMapLayers.remove( myId );
2316  if ( lyr->parent() == this )
2317  {
2318  delete lyr;
2319  }
2320  emit layerRemoved( myId );
2321  }
2322 
2323  emit layersRemoved( layerIds );
2324 }
2325 
2326 void QgsProject::removeMapLayer( const QString& theLayerId )
2327 {
2328  removeMapLayers( QList<QgsMapLayer*>() << mMapLayers.value( theLayerId ) );
2329 }
2330 
2332 {
2333  if ( layer )
2334  removeMapLayers( QList<QgsMapLayer*>() << layer );
2335 }
2336 
2338 {
2339  emit removeAll();
2340  // now let all observers know to clear themselves,
2341  // and then consequently any of their map legends
2342  removeMapLayers( mMapLayers.keys() );
2343  mMapLayers.clear();
2344 }
2345 
2347 {
2348  Q_FOREACH ( QgsMapLayer* layer, mMapLayers )
2349  {
2350  layer->reload();
2351  }
2352 }
2353 
2354 void QgsProject::onMapLayerDeleted( QObject* obj )
2355 {
2356  QString id = mMapLayers.key( static_cast<QgsMapLayer*>( obj ) );
2357 
2358  if ( !id.isNull() )
2359  {
2360  QgsDebugMsg( QString( "Map layer deleted without unregistering! %1" ).arg( id ) );
2361  mMapLayers.remove( id );
2362  }
2363 }
2364 
2365 QMap<QString, QgsMapLayer*> QgsProject::mapLayers() const
2366 {
2367  return mMapLayers;
2368 }
void updateFields()
Assembles mUpdatedFields considering provider fields, joined fields and added fields.
Layer tree group node serves as a container for layers and further groups.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:381
QgsMapLayer * addMapLayer(QgsMapLayer *theMapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
void legendLayersAdded(const QList< QgsMapLayer *> &theMapLayers)
Emitted, when a layer was added to the registry and the legend.
QString error() const
Return error message from previous read/write.
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:376
Base class for all map layer types.
Definition: qgsmaplayer.h:50
void layersAdded(const QList< QgsMapLayer *> &theMapLayers)
Emitted when one or more layers were added to the registry.
static void removeInvalidLayers(QgsLayerTreeGroup *group)
Remove layer nodes that refer to invalid layers.
static const QString QGIS_VERSION
Version string.
Definition: qgis.h:47
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=nullptr) const
void loadingLayer(const QString &)
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer *> &layers)
A list of layers with which intersections should be avoided.
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
Definition: qgsproject.cpp:478
QgsMapLayer * mapLayer(const QString &theLayerId) const
Retrieve a pointer to a registered layer by layer ID.
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
void readChildrenFromXml(QDomElement &element)
Read children from XML and append them to the group.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
Definition: qgsproject.cpp:616
bool readLayerXml(const QDomElement &layerElement)
Sets state from Dom document.
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
static Q_INVOKABLE AreaUnit decodeAreaUnit(const QString &string, bool *ok=0)
Decodes an areal unit from a string.
void removeAllChildren()
Remove all child nodes. The nodes will be deleted.
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
Class used to work with layer dependencies stored in a XML project or layer definition file...
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer *> &theMapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
virtual void reload()
Synchronises with changes in the datasource.
Definition: qgsmaplayer.h:296
QString writePath(const QString &filename, const QString &relativeBasePath=QString::null) const
Prepare a filename to save it to the project file.
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
QString toProj4() const
Returns a Proj4 string representation of this CRS.
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:386
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
void layerWillBeRemoved(const QString &theLayerId)
Emitted when a layer is about to be removed from the registry.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application&#39;s plugin layer registry, used for managing plugin layer types.
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
void topologicalEditingChanged()
Emitted when the topological editing flag has changed.
int count() const
Returns the number of registered layers.
QgsLayerTreeGroup * layerTreeRoot() const
Return pointer to the root (invisible) node of the project&#39;s layer tree.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
void reloadAllLayers()
Reload all registered layer&#39;s provider data caches, synchronising the layer with any changes in the d...
QgsRelationManager * relationManager() const
bool isValid() const
Return the status of the layer.
const QList< QgsVectorJoinInfo > vectorJoins() const
void removeKey(const QString &keyName)
Removes the specified key.
void projectSaved()
emitted when the project file has been written and closed
QStringList nonIdentifiableLayers() const
Get the list of layers which currently should not be taken into account on map identification.
void reset()
reset to default values
void clear()
Remove any relation managed by this class.
QgsProject(QObject *parent=nullptr)
Create a new QgsProject.
Definition: qgsproject.cpp:317
void createJoinCaches() const
Caches joined attributes if required (and not already done)
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
QgsProjectVersion getVersion(const QDomDocument &doc)
Return the version string found in the given DOM document.
Definition: qgsproject.cpp:585
const QString GEO_NONE
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.cpp:71
void fileNameChanged()
Emitted when the file name of the project changes.
void snappingConfigChanged()
emitted whenever the configuration for snapping has changed
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
QgsProjectProperty * find(const QString &propertyName) const
Attempts to find a property with a matching sub-key name.
void setNonIdentifiableLayers(const QList< QgsMapLayer *> &layers)
Set a list of layers which should not be taken into account on map identification.
QList< QgsVectorLayer * > avoidIntersectionsLayers() const
A list of layers with which intersections should be avoided.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean entry to the project file.
void removeMapLayer(const QString &theLayerId)
Remove a layer from the registry by layer ID.
QgsProjectPropertyValue * setValue(const QString &name, const QVariant &value)
Sets the value associated with this key.
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList, bool saveFlag=true)
Creates a maplayer instance defined in an arbitrary project file.
bool read()
Reads the project from its currently associated file (see fileName() ).
Definition: qgsproject.cpp:767
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value)
Add the given key and value.
Definition: qgsproject.cpp:188
QString name() const
The name of the property is used as identifier.
static void _getTitle(const QDomDocument &doc, QString &title)
Get the project title.
Definition: qgsproject.cpp:551
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the default area measurement units for the project.
void removeMapLayers(const QStringList &theLayerIds)
Remove a set of layers from the registry by layer ID.
A class to describe the version of a project.
QString layerIsEmbedded(const QString &id) const
Returns project file path if layer is embedded from other project file. Returns empty string if layer...
int count() const
Returns the number of sub-keys contained by this property.
virtual void clearKeys()
Deletes any sub-nodes from the property.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
void dumpProperties() const
Dump out current project properties to stderr.
void readProject(const QDomDocument &)
emitted when project is being read
Listens to the updates in map layer registry and does changes in layer tree.
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
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...
QStringList subkeyList(const QString &scope, const QString &key) const
Return keys with keys – do not return keys that contain only values.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the project&#39;s native coordinate reference system.
Definition: qgsproject.cpp:424
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
void layerLoaded(int i, int n)
Emitted when a layer from a projects was read.
virtual void writeXml(QDomElement &parentElement)=0
Write layer tree to XML.
void dump(int tabs=0) const override
Dumps out the keys and values.
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the default distance measurement units for the project.
This class is a base class for nodes in a layer tree.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group)
Reads and writes project states.
Definition: qgsproject.h:72
void setEllipsoid(const QString &ellipsoid)
Sets the project&#39;s ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:437
void insertChildNodes(int index, const QList< QgsLayerTreeNode *> &nodes)
Insert existing nodes at specified position. The nodes must not have a parent yet. The nodes will be owned by this group.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if a the provider of a give layer supports transactions.
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted, when a layer is being saved.
void removeAllMapLayers()
Removes all registered layers.
QList< QgsLayerTreeNode * > children()
Get 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...
virtual bool isValue() const =0
Returns true if the property is a QgsProjectPropertyValue.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Definition: qgsproject.cpp:257
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
void layerRemoved(const QString &theLayerId)
Emitted after a layer was removed from the registry.
void setProviderProperty(ProviderProperty property, const QVariant &value)
Allows setting arbitrary properties on the provider.
bool write()
Writes the project to its current associated file (see fileName() ).
#define QgsDebugCall
Definition: qgslogger.h:35
QStringList makeKeyTokens_(const QString &scope, const QString &key)
Take the given scope and key and convert them to a string list of key tokens that will be used to nav...
Definition: qgsproject.cpp:71
bool removeLayers(const QList< QgsMapLayer *> &layers)
Removes the specified layers from the individual layer configuration.
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers)
Create layer group instance defined in an arbitrary project file.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
bool topologicalEditing() const
Convenience function to query topological editing status.
bool readLayer(const QDomNode &layerNode)
Reads the layer described in the associated DOM node.
QString homePath() const
Return project&#39;s home path.
QString uri(bool expandAuthConfig=true) const
return complete uri
bool isEmpty() const
Returns true if this property contains no sub-keys.
QgsMapThemeCollection * mapThemeCollection()
Returns pointer to the project&#39;s map theme collection.
QString fileName() const
Returns the project&#39;s file name.
Project property key node.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:43
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed...
void writeProject(QDomDocument &)
emitted when project is being written
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:532
void oldProjectVersionWarning(const QString &)
emitted when an old project file is read.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
QFileInfo fileInfo() const
Returns QFileInfo object for the project&#39;s associated file.
Definition: qgsproject.cpp:408
virtual QString dump() const override
Return text representation of the tree. For debugging purposes only.
void layersRemoved(const QStringList &theLayerIds)
Emitted after one or more layers were removed from the registry.
bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
QString source() const
Returns the source for the layer.
virtual QgsLayerTreeGroup * clone() const override
Return a clone of the group. The children are cloned too.
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed...
This class manages a set of relations between layers.
void nonIdentifiableLayersChanged(QStringList nonIdentifiableLayers)
Emitted when the list of layer which are excluded from map identification changes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name. Searches recursively the whole sub-tree.
QgsSnappingConfig snappingConfig() const
The snapping configuration for this project.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:350
QString qgsVsiPrefix(const QString &path)
Definition: qgis.cpp:209
This class represents a coordinate reference system (CRS).
void setTitle(const QString &title)
Sets the project&#39;s title.
Definition: qgsproject.cpp:359
QgsCoordinateReferenceSystem crs() const
Returns the project&#39;s native coordinate reference system.
QString readPath(QString filename) const
Turn filename read from the project file to an absolute path.
An Abstract Base Class for QGIS project property hierarchys.
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QString &relativeBasePath=QString::null) const
Stores state in Dom node.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
void homePathChanged()
Emitted when the home path of the project changes.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
QString name
Read property of QString layerName.
Definition: qgsmaplayer.h:54
void subkeyList(QStringList &entries) const
Return any sub-keys contained by this property which themselves contain other keys.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
QString name() const override
Get group&#39;s name.
void avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes. Searches recursively the whole sub-tree.
Container class that allows storage of map themes consisting of visible map layers and layer styles...
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:370
This is a container for configuration of the snapping of the project.
QgsUnitTypes::DistanceUnit distanceUnits() const
Convenience function to query default distance measurement units for project.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
QVariantMap customVariables() const
A map of custom project variables.
Definition: qgsproject.cpp:997
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted, after the basic initialization of a layer from the project file is done. ...
QString ellipsoid() const
Returns a proj string representing the project&#39;s ellipsoid setting, e.g., "WGS84".
Definition: qgsproject.cpp:432
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QString providerType() const
Return the provider type for this layer.
long srsid() const
Returns the internal CRS ID, if available.
bool addLayers(const QList< QgsMapLayer *> &layers)
Adds the specified layers as individual layers to the configuration with standard configuration...
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:113
void setEvaluateDefaultValues(bool evaluateDefaultValues)
Defines if default values should be evaluated on provider side when requested and not when committed...
Represents a vector layer which manages a vector based data sets.
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
void setName(const QString &name)
The name of the property is used as identifier.
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
AreaUnit
Units of area.
Definition: qgsunittypes.h:65
bool isNull(const QVariant &v)
QString database() const
Returns the database.
void layersWillBeRemoved(const QStringList &theLayerIds)
Emitted when one or more layers are about to be removed from the registry.
QStringList entryList(const QString &scope, const QString &key) const
Return keys with values – do not return keys that contain other keys.
QgsPluginLayer * createLayer(const QString &typeName, const QString &uri=QString())
Return new layer if corresponding plugin has been found, else return NULL.
void removeAll()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void _getProperties(const QDomDocument &doc, QgsProjectPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:515
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
QString authid() const
Returns the authority identifier for the CRS.
void setDatabase(const QString &database)
Set database.
void clear()
Clear the project - removes all settings and resets it back to an empty, default state.
Definition: qgsproject.cpp:443
Interface for classes that handle missing layer files when reading project file.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=0)
Decodes a distance unit from a string.
void layerWasAdded(QgsMapLayer *theMapLayer)
Emitted when a layer was added to the registry.
Layer tree node points to a map layer.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node. Properties are stored in a map and saved in project file...