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