QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgsprojectproperty.h"
32 #include "qgsprojectversion.h"
33 #include "qgsrasterlayer.h"
34 #include "qgsrectangle.h"
35 #include "qgsrelationmanager.h"
36 #include "qgsvectorlayer.h"
38 #include "qgslayerdefinition.h"
39 #include "qgsunittypes.h"
40 #include "qgstransaction.h"
41 #include "qgstransactiongroup.h"
42 #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::theProject_ = 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( "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 = QString( "[^: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 = QString( "^[^: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 QgsProperty *findKey_( const QString& scope,
114  const QString& key,
115  QgsPropertyKey& rootProperty )
116 {
117  QgsPropertyKey *currentProperty = &rootProperty;
118  QgsProperty *nextProperty; // link to next property down hiearchy
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<QgsPropertyKey*>( 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  // QgsPropertyValue 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 QgsProperty *addKey_( const QString& scope,
189  const QString& key,
190  QgsPropertyKey* rootProperty,
191  const QVariant& value )
192 {
193  QStringList keySequence = makeKeyTokens_( scope, key );
194 
195  // cursor through property key/value hierarchy
196  QgsPropertyKey *currentProperty = rootProperty;
197  QgsProperty *nextProperty; // link to next property down hiearchy
198  QgsPropertyKey* 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<QgsPropertyKey*>( nextProperty );
227 
228  if ( currentProperty )
229  {
230  continue;
231  }
232  else // QgsPropertyValue 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  QgsPropertyKey &rootProperty )
260 {
261  QgsPropertyKey *currentProperty = &rootProperty;
262 
263  QgsProperty *nextProperty = nullptr; // link to next property down hiearchy
264  QgsPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hiearchy
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<QgsPropertyKey*>( nextProperty );
294 
295  if ( currentProperty )
296  {
297  continue;
298  }
299  else // QgsPropertyValue 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 
318 // TODO QGIS 3.0 - move to private members. This pattern is adding complexity without any current benefits.
319 
321 {
322  QFile file; // current physical project file
323  QgsPropertyKey properties_; // property hierarchy
324  QString title; // project title
325  bool autoTransaction; // transaction grouped editing
326  bool evaluateDefaultValues; // evaluate default values immediately
327  bool dirty; // project has been modified since it has been read or saved
328 
329  Imp()
330  : title()
331  , autoTransaction( false )
332  , evaluateDefaultValues( false )
333  , dirty( false )
334  { // top property node is the root
335  // "properties" that contains all plug-in
336  // and extra property keys and values
337  properties_.name() = "properties"; // root property node always this value
338  }
339 
342  void clear()
343  {
344  // QgsDebugMsg( "Clearing project properties Impl->clear();" );
345 
346  file.setFileName( QString() );
347  properties_.clearKeys();
348  title.clear();
349  autoTransaction = false;
350  evaluateDefaultValues = false;
351  dirty = false;
352  }
353 
354 };
355 
356 
357 QgsProject::QgsProject()
358  : imp_( new QgsProject::Imp )
359  , mBadLayerHandler( new QgsProjectBadLayerDefaultHandler() )
360  , mRelationManager( new QgsRelationManager( this ) )
361  , mRootGroup( new QgsLayerTreeGroup )
362 {
363  clear();
364 
365  // bind the layer tree to the map layer registry.
366  // whenever layers are added to or removed from the registry,
367  // layer tree will be updated
368  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this );
369  connect( QgsMapLayerRegistry::instance(), SIGNAL( layersAdded( QList<QgsMapLayer*> ) ), this, SLOT( onMapLayersAdded( QList<QgsMapLayer*> ) ) );
370  connect( QgsMapLayerRegistry::instance(), SIGNAL( layersRemoved( QStringList ) ), this, SLOT( cleanTransactionGroups() ) );
371 }
372 
373 
375 {
376  delete mBadLayerHandler;
377  delete mRelationManager;
378  delete mRootGroup;
379 }
380 
381 
383 {
384  if ( !theProject_ )
385  {
386  theProject_ = new QgsProject;
387  }
388  return theProject_;
389 }
390 
392 {
393  imp_->title = title;
394 
395  setDirty( true );
396 }
397 
398 
400 {
401  return imp_->title;
402 }
403 
404 
406 {
407  return imp_->dirty;
408 }
409 
410 void QgsProject::setDirty( bool b )
411 {
412  imp_->dirty = b;
413 }
414 
416 {
417  emit variablesChanged();
418 }
419 
421 {
422  if ( name == imp_->file.fileName() )
423  return;
424 
425  QString oldHomePath = homePath();
426 
427  imp_->file.setFileName( name );
428  emit fileNameChanged();
429 
430  QString newHomePath = homePath();
431  if ( newHomePath != oldHomePath )
432  emit homePathChanged();
433 
434  setDirty( true );
435 }
436 
438 {
439  return imp_->file.fileName();
440 }
441 
443 {
444  return QFileInfo( imp_->file );
445 }
446 
448 {
449  imp_->clear();
450  mEmbeddedLayers.clear();
451  mRelationManager->clear();
452 
453  mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
454 
455  mRootGroup->removeAllChildren();
456 
457  // reset some default project properties
458  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
459  writeEntry( "PositionPrecision", "/Automatic", true );
460  writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
461  writeEntry( "Paths", "/Absolute", false );
462 
463  //copy default units to project
464  QSettings s;
465  writeEntry( "Measurement", "/DistanceUnits", s.value( "/qgis/measure/displayunits" ).toString() );
466  writeEntry( "Measurement", "/AreaUnits", s.value( "/qgis/measure/areaunits" ).toString() );
467 
468  setDirty( false );
469 }
470 
471 // basically a debugging tool to dump property list values
472 void dump_( const QgsPropertyKey& topQgsPropertyKey )
473 {
474  QgsDebugMsg( "current properties:" );
475  topQgsPropertyKey.dump();
476 }
477 
478 
509 void _getProperties( const QDomDocument& doc, QgsPropertyKey& project_properties )
510 {
511  QDomNodeList properties = doc.elementsByTagName( "properties" );
512 
513  if ( properties.count() > 1 )
514  {
515  QgsDebugMsg( "there appears to be more than one ``properties'' XML tag ... bailing" );
516  return;
517  }
518  else if ( properties.count() < 1 ) // no properties found, so we're done
519  {
520  return;
521  }
522 
523  // item(0) because there should only be ONE "properties" node
524  QDomNodeList scopes = properties.item( 0 ).childNodes();
525 
526  if ( scopes.count() < 1 )
527  {
528  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
529  return;
530  }
531 
532  QDomNode propertyNode = properties.item( 0 );
533 
534  if ( ! project_properties.readXML( propertyNode ) )
535  {
536  QgsDebugMsg( "Project_properties.readXML() failed" );
537  }
538 }
539 
540 
545 static void _getTitle( const QDomDocument& doc, QString& title )
546 {
547  QDomNodeList nl = doc.elementsByTagName( "title" );
548 
549  title = ""; // by default the title will be empty
550 
551  if ( !nl.count() )
552  {
553  QgsDebugMsg( "unable to find title element" );
554  return;
555  }
556 
557  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element ok
558 
559  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
560  {
561  QgsDebugMsg( "unable to find title element" );
562  return;
563  }
564 
565  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
566 
567  if ( !titleTextNode.isText() )
568  {
569  QgsDebugMsg( "unable to find title element" );
570  return;
571  }
572 
573  QDomText titleText = titleTextNode.toText();
574 
575  title = titleText.data();
576 
577 }
578 
580 {
581  QDomNodeList nl = doc.elementsByTagName( "qgis" );
582 
583  if ( !nl.count() )
584  {
585  QgsDebugMsg( " unable to find qgis element in project file" );
586  return QgsProjectVersion( 0, 0, 0, QString() );
587  }
588 
589  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
590 
591  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
592  QgsProjectVersion projectVersion( qgisElement.attribute( "version" ) );
593  return projectVersion;
594 }
595 
596 void QgsProject::processLayerJoins( QgsVectorLayer* layer )
597 {
598  if ( !layer )
599  return;
600 
601  layer->createJoinCaches();
602  layer->updateFields();
603 }
604 
605 bool QgsProject::_getMapLayers( const QDomDocument& doc, QList<QDomNode>& brokenNodes )
606 {
607  // Layer order is set by the restoring the legend settings from project file.
608  // This is done on the 'readProject( ... )' signal
609 
610  QDomNodeList nl = doc.elementsByTagName( "maplayer" );
611 
612  // process the map layer nodes
613 
614  if ( 0 == nl.count() ) // if we have no layers to process, bail
615  {
616  return true; // Decided to return "true" since it's
617  // possible for there to be a project with no
618  // layers; but also, more imporantly, this
619  // would cause the tests/qgsproject to fail
620  // since the test suite doesn't currently
621  // support test layers
622  }
623 
624  bool returnStatus = true;
625 
626  emit layerLoaded( 0, nl.count() );
627 
628  // order layers based on their dependencies
629  QgsLayerDefinition::DependencySorter depSorter( doc );
630  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
631  return false;
632 
633  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
634 
635  // Collect vector layers with joins.
636  // They need to refresh join caches and symbology infos after all layers are loaded
638  int i = 0;
639  Q_FOREACH ( const QDomNode& node, sortedLayerNodes )
640  {
641  QDomElement element = node.toElement();
642 
643  QString name = node.namedItem( "layername" ).toElement().text();
644  if ( !name.isNull() )
645  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
646 
647  if ( element.attribute( "embedded" ) == "1" )
648  {
649  createEmbeddedLayer( element.attribute( "id" ), readPath( element.attribute( "project" ) ), brokenNodes, vLayerList );
650  continue;
651  }
652  else
653  {
654  if ( !addLayer( element, brokenNodes, vLayerList ) )
655  {
656  returnStatus = false;
657  }
658  }
659  emit layerLoaded( i + 1, nl.count() );
660  i++;
661  }
662 
663  // Update field map of layers with joins and create join caches if necessary
664  // Needs to be done here once all dependent layers are loaded
665  QList< QPair< QgsVectorLayer*, QDomElement > >::iterator vIt = vLayerList.begin();
666  for ( ; vIt != vLayerList.end(); ++vIt )
667  {
668  processLayerJoins( vIt->first );
669  }
670 
671  QSet<QgsVectorLayer *> notified;
672  for ( vIt = vLayerList.begin(); vIt != vLayerList.end(); ++vIt )
673  {
674  if ( notified.contains( vIt->first ) )
675  continue;
676 
677  notified << vIt->first;
678  emit readMapLayer( vIt->first, vIt->second );
679  }
680 
681  return returnStatus;
682 }
683 
684 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList )
685 {
686  QString type = layerElem.attribute( "type" );
687  QgsDebugMsg( "Layer type is " + type );
688  QgsMapLayer *mapLayer = nullptr;
689 
690  if ( type == "vector" )
691  {
692  mapLayer = new QgsVectorLayer;
693  }
694  else if ( type == "raster" )
695  {
696  mapLayer = new QgsRasterLayer;
697  }
698  else if ( type == "plugin" )
699  {
700  QString typeName = layerElem.attribute( "name" );
701  mapLayer = QgsPluginLayerRegistry::instance()->createLayer( typeName );
702  }
703 
704  if ( !mapLayer )
705  {
706  QgsDebugMsg( "Unable to create layer" );
707 
708  return false;
709  }
710 
711  Q_CHECK_PTR( mapLayer );
712 
713  // have the layer restore state that is stored in Dom node
714  if ( mapLayer->readLayerXML( layerElem ) && mapLayer->isValid() )
715  {
716  // postpone readMapLayer signal for vector layers with joins
717  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
718  if ( !vLayer || vLayer->vectorJoins().isEmpty() )
719  emit readMapLayer( mapLayer, layerElem );
720  else
721  vectorLayerList.push_back( qMakePair( vLayer, layerElem ) );
722 
723  QList<QgsMapLayer *> myLayers;
724  myLayers << mapLayer;
726 
727  return true;
728  }
729  else
730  {
731  delete mapLayer;
732 
733  QgsDebugMsg( "Unable to load " + type + " layer" );
734  brokenNodes.push_back( layerElem );
735  return false;
736  }
737 }
738 
740 {
741  imp_->file.setFileName( file.filePath() );
742 
743  return read();
744 }
745 
747 {
748  clearError();
749 
750  QScopedPointer<QDomDocument> doc( new QDomDocument( "qgis" ) );
751 
752  if ( !imp_->file.open( QIODevice::ReadOnly | QIODevice::Text ) )
753  {
754  imp_->file.close();
755 
756  setError( tr( "Unable to open %1" ).arg( imp_->file.fileName() ) );
757 
758  return false;
759  }
760 
761  // location of problem associated with errorMsg
762  int line, column;
763  QString errorMsg;
764 
765  if ( !doc->setContent( &imp_->file, &errorMsg, &line, &column ) )
766  {
767  // want to make this class as GUI independent as possible; so commented out
768 #if 0
769  QMessageBox::critical( 0, tr( "Project File Read Error" ),
770  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
771 #endif
772 
773  QString errorString = tr( "Project file read error: %1 at line %2 column %3" )
774  .arg( errorMsg ).arg( line ).arg( column );
775 
776  QgsDebugMsg( errorString );
777 
778  imp_->file.close();
779 
780  setError( tr( "%1 for file %2" ).arg( errorString, imp_->file.fileName() ) );
781 
782  return false;
783  }
784 
785  imp_->file.close();
786 
787 
788  QgsDebugMsg( "Opened document " + imp_->file.fileName() );
789  QgsDebugMsg( "Project title: " + imp_->title );
790 
791  // get project version string, if any
792  QgsProjectVersion fileVersion = getVersion( *doc );
793  QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
794 
795  if ( thisVersion > fileVersion )
796  {
797  QgsLogger::warning( "Loading a file that was saved with an older "
798  "version of qgis (saved in " + fileVersion.text() +
799  ", loaded in " + QGis::QGIS_VERSION +
800  "). Problems may occur." );
801 
802  QgsProjectFileTransform projectFile( *doc, fileVersion );
803 
805  emit oldProjectVersionWarning( fileVersion.text() );
806  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
807 
808  projectFile.updateRevision( thisVersion );
809  }
810 
811  // start new project, just keep the file name
812  QString fileName = imp_->file.fileName();
813  clear();
814  imp_->file.setFileName( fileName );
815 
816  // now get any properties
817  _getProperties( *doc, imp_->properties_ );
818 
819  QgsDebugMsg( QString::number( imp_->properties_.count() ) + " properties read" );
820 
821  dump_( imp_->properties_ );
822 
823  // now get project title
824  _getTitle( *doc, imp_->title );
825 
826  QDomNodeList nl = doc->elementsByTagName( "autotransaction" );
827  if ( nl.count() )
828  {
829  QDomElement transactionElement = nl.at( 0 ).toElement();
830  if ( transactionElement.attribute( "active", "0" ).toInt() == 1 )
831  imp_->autoTransaction = true;
832  }
833 
834  nl = doc->elementsByTagName( "evaluateDefaultValues" );
835  if ( nl.count() )
836  {
837  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
838  if ( evaluateDefaultValuesElement.attribute( "active", "0" ).toInt() == 1 )
839  imp_->evaluateDefaultValues = true;
840  }
841 
842  // read the layer tree from project file
843 
844  mRootGroup->setCustomProperty( "loading", 1 );
845 
846  QDomElement layerTreeElem = doc->documentElement().firstChildElement( "layer-tree-group" );
847  if ( !layerTreeElem.isNull() )
848  {
849  mRootGroup->readChildrenFromXML( layerTreeElem );
850  }
851  else
852  {
853  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( "legend" ) );
854  }
855 
856  QgsDebugMsg( "Loaded layer tree:\n " + mRootGroup->dump() );
857 
858  mLayerTreeRegistryBridge->setEnabled( false );
859 
860  // get the map layers
861  QList<QDomNode> brokenNodes;
862  bool clean = _getMapLayers( *doc, brokenNodes );
863 
864  // review the integrity of the retrieved map layers
865  if ( !clean )
866  {
867  QgsDebugMsg( "Unable to get map layers from project file." );
868 
869  if ( !brokenNodes.isEmpty() )
870  {
871  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
872  }
873 
874  // we let a custom handler to decide what to do with missing layers
875  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
876  mBadLayerHandler->handleBadLayers( brokenNodes, *doc );
877  }
878 
879  mLayerTreeRegistryBridge->setEnabled( true );
880 
881  // load embedded groups and layers
882  loadEmbeddedNodes( mRootGroup );
883 
884  // make sure the are just valid layers
886 
887  mRootGroup->removeCustomProperty( "loading" );
888 
889  mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
890  mVisibilityPresetCollection->readXML( *doc );
891 
892 
893  // read the project: used by map canvas and legend
894  emit readProject( *doc );
895 
896  // if all went well, we're allegedly in pristine state
897  if ( clean )
898  setDirty( false );
899 
901 
902  return true;
903 }
904 
905 
907 {
908  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
909  {
910  if ( QgsLayerTree::isGroup( child ) )
911  {
912  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
913  if ( childGroup->customProperty( "embedded" ).toInt() )
914  {
915  // make sure to convert the path from relative to absolute
916  QString projectPath = readPath( childGroup->customProperty( "embedded_project" ).toString() );
917  childGroup->setCustomProperty( "embedded_project", projectPath );
918 
919  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( "embedded-invisible-layers" ).toStringList() );
920  if ( newGroup )
921  {
922  QList<QgsLayerTreeNode*> clonedChildren;
923  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
924  clonedChildren << newGroupChild->clone();
925  delete newGroup;
926 
927  childGroup->insertChildNodes( 0, clonedChildren );
928  }
929  }
930  else
931  {
932  loadEmbeddedNodes( childGroup );
933  }
934  }
935  else if ( QgsLayerTree::isLayer( child ) )
936  {
937  if ( child->customProperty( "embedded" ).toInt() )
938  {
939  QList<QDomNode> brokenNodes;
941  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( "embedded_project" ).toString(), brokenNodes, vectorLayerList );
942  }
943  }
944 
945  }
946 }
947 
948 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer*>& layers )
949 {
950  Q_FOREACH ( QgsMapLayer* layer, layers )
951  {
952  QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( layer );
953  if ( vlayer )
954  {
955  if ( autoTransaction() )
956  {
957  if ( QgsTransaction::supportsTransaction( vlayer ) )
958  {
959  QString connString = QgsDataSourceURI( vlayer->source() ).connectionInfo();
960  QString key = vlayer->providerType();
961 
962  QgsTransactionGroup* tg = mTransactionGroups.value( qMakePair( key, connString ) );
963 
964  if ( !tg )
965  {
966  tg = new QgsTransactionGroup();
967  mTransactionGroups.insert( qMakePair( key, connString ), tg );
968 
969  connect( tg, SIGNAL( commitError( QString ) ), this, SLOT( displayMapToolMessage( QString ) ) );
970  }
971  tg->addLayer( vlayer );
972  }
973  }
975  }
976 
977  connect( layer, SIGNAL( configChanged() ), this, SLOT( setDirty() ) );
978  }
979 }
980 
981 void QgsProject::cleanTransactionGroups( bool force )
982 {
983  for ( QMap< QPair< QString, QString>, QgsTransactionGroup*>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
984  {
985  if ( tg.value()->isEmpty() || force )
986  {
987  delete tg.value();
988  tg = mTransactionGroups.erase( tg );
989  }
990  else
991  {
992  ++tg;
993  }
994  }
995 }
996 
997 bool QgsProject::read( QDomNode &layerNode )
998 {
999  QList<QDomNode> brokenNodes;
1001  if ( addLayer( layerNode.toElement(), brokenNodes, vectorLayerList ) )
1002  {
1003  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1004  // added layer for joins
1006  Q_FOREACH ( QgsVectorLayer* layer, vectorLayers )
1007  {
1008  processLayerJoins( layer );
1009  }
1010 
1011  if ( !vectorLayerList.isEmpty() )
1012  {
1013  emit readMapLayer( vectorLayerList.at( 0 ).first, vectorLayerList.at( 0 ).second );
1014  }
1015 
1016  return true;
1017  }
1018  return false;
1019 }
1020 
1022 {
1023  imp_->file.setFileName( file.filePath() );
1024 
1025  return write();
1026 }
1027 
1029 {
1030  clearError();
1031 
1032  // if we have problems creating or otherwise writing to the project file,
1033  // let's find out up front before we go through all the hand-waving
1034  // necessary to create all the Dom objects
1035  QFileInfo myFileInfo( imp_->file );
1036  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1037  {
1038  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1039  .arg( imp_->file.fileName() ) );
1040  return false;
1041  }
1042 
1043  QDomImplementation DomImplementation;
1044  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1045 
1046  QDomDocumentType documentType =
1047  DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd",
1048  "SYSTEM" );
1049  QScopedPointer<QDomDocument> doc( new QDomDocument( documentType ) );
1050 
1051  QDomElement qgisNode = doc->createElement( "qgis" );
1052  qgisNode.setAttribute( "projectname", title() );
1053  qgisNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
1054 
1055  doc->appendChild( qgisNode );
1056 
1057  // title
1058  QDomElement titleNode = doc->createElement( "title" );
1059  qgisNode.appendChild( titleNode );
1060 
1061  QDomElement transactionNode = doc->createElement( "autotransaction" );
1062  transactionNode.setAttribute( "active", imp_->autoTransaction ? "1" : "0" );
1063  qgisNode.appendChild( transactionNode );
1064 
1065  QDomElement evaluateDefaultValuesNode = doc->createElement( "evaluateDefaultValues" );
1066  evaluateDefaultValuesNode.setAttribute( "active", imp_->evaluateDefaultValues ? "1" : "0" );
1067  qgisNode.appendChild( evaluateDefaultValuesNode );
1068 
1069  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1070  titleNode.appendChild( titleText );
1071 
1072  // write layer tree - make sure it is without embedded subgroups
1073  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1075  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ) ); // convert absolute paths to relative paths if required
1076  clonedRoot->writeXML( qgisNode );
1077  delete clonedRoot;
1078 
1079  // let map canvas and legend write their information
1080  emit writeProject( *doc );
1081 
1082  // within top level node save list of layers
1084 
1085  // Iterate over layers in zOrder
1086  // Call writeXML() on each
1087  QDomElement projectLayersNode = doc->createElement( "projectlayers" );
1088 
1090  while ( li != layers.end() )
1091  {
1092  QgsMapLayer *ml = li.value();
1093 
1094  if ( ml )
1095  {
1096  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1097  if ( emIt == mEmbeddedLayers.constEnd() )
1098  {
1099  // general layer metadata
1100  QDomElement maplayerElem = doc->createElement( "maplayer" );
1101 
1102  ml->writeLayerXML( maplayerElem, *doc );
1103 
1104  emit writeMapLayer( ml, maplayerElem, *doc );
1105 
1106  projectLayersNode.appendChild( maplayerElem );
1107  }
1108  else
1109  {
1110  // layer defined in an external project file
1111  // only save embedded layer if not managed by a legend group
1112  if ( emIt.value().second )
1113  {
1114  QDomElement mapLayerElem = doc->createElement( "maplayer" );
1115  mapLayerElem.setAttribute( "embedded", 1 );
1116  mapLayerElem.setAttribute( "project", writePath( emIt.value().first ) );
1117  mapLayerElem.setAttribute( "id", ml->id() );
1118  projectLayersNode.appendChild( mapLayerElem );
1119  }
1120  }
1121  }
1122  li++;
1123  }
1124 
1125  qgisNode.appendChild( projectLayersNode );
1126 
1127  // now add the optional extra properties
1128 
1129  dump_( imp_->properties_ );
1130 
1131  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( imp_->properties_.count() ) ) );
1132 
1133  if ( !imp_->properties_.isEmpty() ) // only worry about properties if we
1134  // actually have any properties
1135  {
1136  imp_->properties_.writeXML( "properties", qgisNode, *doc );
1137  }
1138 
1139  mVisibilityPresetCollection->writeXML( *doc );
1140 
1141  // now wrap it up and ship it to the project file
1142  doc->normalize(); // XXX I'm not entirely sure what this does
1143 
1144  // Create backup file
1145  if ( QFile::exists( fileName() ) )
1146  {
1147  QFile backupFile( fileName() + '~' );
1148  bool ok = true;
1149  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1150  ok &= imp_->file.open( QIODevice::ReadOnly );
1151 
1152  QByteArray ba;
1153  while ( ok && !imp_->file.atEnd() )
1154  {
1155  ba = imp_->file.read( 10240 );
1156  ok &= backupFile.write( ba ) == ba.size();
1157  }
1158 
1159  imp_->file.close();
1160  backupFile.close();
1161 
1162  if ( !ok )
1163  {
1164  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1165  return false;
1166  }
1167 
1168  QFileInfo fi( fileName() );
1169  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1170  utime( backupFile.fileName().toUtf8().constData(), &tb );
1171  }
1172 
1173  if ( !imp_->file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1174  {
1175  imp_->file.close(); // even though we got an error, let's make
1176  // sure it's closed anyway
1177 
1178  setError( tr( "Unable to save to file %1" ).arg( imp_->file.fileName() ) );
1179  return false;
1180  }
1181 
1182  QTemporaryFile tempFile;
1183  bool ok = tempFile.open();
1184  if ( ok )
1185  {
1186  QTextStream projectFileStream( &tempFile );
1187  doc->save( projectFileStream, 2 ); // save as utf-8
1188  ok &= projectFileStream.pos() > -1;
1189 
1190  ok &= tempFile.seek( 0 );
1191 
1192  QByteArray ba;
1193  while ( ok && !tempFile.atEnd() )
1194  {
1195  ba = tempFile.read( 10240 );
1196  ok &= imp_->file.write( ba ) == ba.size();
1197  }
1198 
1199  ok &= imp_->file.error() == QFile::NoError;
1200 
1201  imp_->file.close();
1202  }
1203 
1204  tempFile.close();
1205 
1206  if ( !ok )
1207  {
1208  setError( tr( "Unable to save to file %1. Your project "
1209  "may be corrupted on disk. Try clearing some space on the volume and "
1210  "check file permissions before pressing save again." )
1211  .arg( imp_->file.fileName() ) );
1212  return false;
1213  }
1214 
1215  setDirty( false ); // reset to pristine state
1216 
1217  emit projectSaved();
1218 
1219  return true;
1220 }
1221 
1223 {
1224  clear();
1225 
1226  setDirty( true );
1227 }
1228 
1229 bool QgsProject::writeEntry( const QString& scope, QString const& key, bool value )
1230 {
1231  setDirty( true );
1232 
1233  return addKey_( scope, key, &imp_->properties_, value );
1234 }
1235 
1236 bool QgsProject::writeEntry( const QString& scope, const QString& key,
1237  double value )
1238 {
1239  setDirty( true );
1240 
1241  return addKey_( scope, key, &imp_->properties_, value );
1242 }
1243 
1244 bool QgsProject::writeEntry( const QString& scope, QString const& key, int value )
1245 {
1246  setDirty( true );
1247 
1248  return addKey_( scope, key, &imp_->properties_, value );
1249 }
1250 
1251 bool QgsProject::writeEntry( const QString& scope, const QString &key,
1252  const QString& value )
1253 {
1254  setDirty( true );
1255 
1256  return addKey_( scope, key, &imp_->properties_, value );
1257 }
1258 
1259 bool QgsProject::writeEntry( const QString& scope, const QString& key,
1260  const QStringList& value )
1261 {
1262  setDirty( true );
1263 
1264  return addKey_( scope, key, &imp_->properties_, value );
1265 }
1266 
1268  const QString &key,
1269  const QStringList& def,
1270  bool *ok ) const
1271 {
1272  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1273 
1274  QVariant value;
1275 
1276  if ( property )
1277  {
1278  value = property->value();
1279 
1280  bool valid = QVariant::StringList == value.type();
1281  if ( ok )
1282  *ok = valid;
1283 
1284  if ( valid )
1285  {
1286  return value.toStringList();
1287  }
1288  }
1289 
1290  return def;
1291 }
1292 
1293 
1295  const QString& key,
1296  const QString& def,
1297  bool* ok ) const
1298 {
1299  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1300 
1301  QVariant value;
1302 
1303  if ( property )
1304  {
1305  value = property->value();
1306 
1307  bool valid = value.canConvert( QVariant::String );
1308  if ( ok )
1309  *ok = valid;
1310 
1311  if ( valid )
1312  return value.toString();
1313  }
1314 
1315  return def;
1316 }
1317 
1318 int QgsProject::readNumEntry( const QString& scope, const QString &key, int def,
1319  bool* ok ) const
1320 {
1321  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1322 
1323  QVariant value;
1324 
1325  if ( property )
1326  {
1327  value = property->value();
1328  }
1329 
1330  bool valid = value.canConvert( QVariant::String );
1331 
1332  if ( ok )
1333  {
1334  *ok = valid;
1335  }
1336 
1337  if ( valid )
1338  {
1339  return value.toInt();
1340  }
1341 
1342  return def;
1343 }
1344 
1345 double QgsProject::readDoubleEntry( const QString& scope, const QString& key,
1346  double def,
1347  bool* ok ) const
1348 {
1349  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1350  if ( property )
1351  {
1352  QVariant value = property->value();
1353 
1354  bool valid = value.canConvert( QVariant::Double );
1355  if ( ok )
1356  *ok = valid;
1357 
1358  if ( valid )
1359  return value.toDouble();
1360  }
1361 
1362  return def;
1363 }
1364 
1365 bool QgsProject::readBoolEntry( const QString& scope, const QString &key, bool def,
1366  bool* ok ) const
1367 {
1368  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1369 
1370  if ( property )
1371  {
1372  QVariant value = property->value();
1373 
1374  bool valid = value.canConvert( QVariant::Bool );
1375  if ( ok )
1376  *ok = valid;
1377 
1378  if ( valid )
1379  return value.toBool();
1380  }
1381 
1382  return def;
1383 }
1384 
1385 
1386 bool QgsProject::removeEntry( const QString& scope, const QString& key )
1387 {
1388  removeKey_( scope, key, imp_->properties_ );
1389 
1390  setDirty( true );
1391 
1392  return !findKey_( scope, key, imp_->properties_ );
1393 }
1394 
1395 
1396 QStringList QgsProject::entryList( const QString& scope, const QString& key ) const
1397 {
1398  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1399 
1400  QStringList entries;
1401 
1402  if ( foundProperty )
1403  {
1404  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1405 
1406  if ( propertyKey )
1407  { propertyKey->entryList( entries ); }
1408  }
1409 
1410  return entries;
1411 }
1412 
1413 QStringList QgsProject::subkeyList( const QString& scope, const QString& key ) const
1414 {
1415  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1416 
1417  QStringList entries;
1418 
1419  if ( foundProperty )
1420  {
1421  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1422 
1423  if ( propertyKey )
1424  { propertyKey->subkeyList( entries ); }
1425  }
1426 
1427  return entries;
1428 }
1429 
1431 {
1432  dump_( imp_->properties_ );
1433 }
1434 
1435 QString QgsProject::readPath( QString src, const QString& relativeBasePath ) const
1436 {
1437  if ( readBoolEntry( "Paths", "/Absolute", false ) )
1438  {
1439  return src;
1440  }
1441 
1442  // if this is a VSIFILE, remove the VSI prefix and append to final result
1443  QString vsiPrefix = qgsVsiPrefix( src );
1444  if ( ! vsiPrefix.isEmpty() )
1445  {
1446  // unfortunately qgsVsiPrefix returns prefix also for files like "/x/y/z.gz"
1447  // so we need to check if we really have the prefix
1448  if ( src.startsWith( "/vsi", Qt::CaseInsensitive ) )
1449  src.remove( 0, vsiPrefix.size() );
1450  else
1451  vsiPrefix.clear();
1452  }
1453 
1454  // relative path should always start with ./ or ../
1455  if ( !src.startsWith( "./" ) && !src.startsWith( "../" ) )
1456  {
1457 #if defined(Q_OS_WIN)
1458  if ( src.startsWith( "\\\\" ) ||
1459  src.startsWith( "//" ) ||
1460  ( src[0].isLetter() && src[1] == ':' ) )
1461  {
1462  // UNC or absolute path
1463  return vsiPrefix + src;
1464  }
1465 #else
1466  if ( src[0] == '/' )
1467  {
1468  // absolute path
1469  return vsiPrefix + src;
1470  }
1471 #endif
1472 
1473  // so this one isn't absolute, but also doesn't start // with ./ or ../.
1474  // That means that it was saved with an earlier version of "relative path support",
1475  // where the source file had to exist and only the project directory was stripped
1476  // from the filename.
1477  QString home = homePath();
1478  if ( home.isNull() )
1479  return vsiPrefix + src;
1480 
1481  QFileInfo fi( home + '/' + src );
1482 
1483  if ( !fi.exists() )
1484  {
1485  return vsiPrefix + src;
1486  }
1487  else
1488  {
1489  return vsiPrefix + fi.canonicalFilePath();
1490  }
1491  }
1492 
1493  QString srcPath = src;
1494  QString projPath = fileName();
1495 
1496  if ( !relativeBasePath.isNull() )
1497  {
1498  projPath = relativeBasePath;
1499  }
1500 
1501  if ( projPath.isEmpty() )
1502  {
1503  return vsiPrefix + src;
1504  }
1505 
1506 #if defined(Q_OS_WIN)
1507  srcPath.replace( '\\', '/' );
1508  projPath.replace( '\\', '/' );
1509 
1510  bool uncPath = projPath.startsWith( "//" );
1511 #endif
1512 
1513  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1514  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1515 
1516 #if defined(Q_OS_WIN)
1517  if ( uncPath )
1518  {
1519  projElems.insert( 0, "" );
1520  projElems.insert( 0, "" );
1521  }
1522 #endif
1523 
1524  // remove project file element
1525  projElems.removeLast();
1526 
1527  // append source path elements
1528  projElems << srcElems;
1529  projElems.removeAll( "." );
1530 
1531  // resolve ..
1532  int pos;
1533  while (( pos = projElems.indexOf( ".." ) ) > 0 )
1534  {
1535  // remove preceding element and ..
1536  projElems.removeAt( pos - 1 );
1537  projElems.removeAt( pos - 1 );
1538  }
1539 
1540 #if !defined(Q_OS_WIN)
1541  // make path absolute
1542  projElems.prepend( "" );
1543 #endif
1544 
1545  return vsiPrefix + projElems.join( "/" );
1546 }
1547 
1548 QString QgsProject::writePath( const QString& src, const QString& relativeBasePath ) const
1549 {
1550  if ( readBoolEntry( "Paths", "/Absolute", false ) || src.isEmpty() )
1551  {
1552  return src;
1553  }
1554 
1555  QFileInfo srcFileInfo( src );
1556  QFileInfo projFileInfo( fileName() );
1557  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
1558  QString projPath = projFileInfo.exists( ) ? projFileInfo.canonicalFilePath() : fileName();
1559 
1560  if ( !relativeBasePath.isNull() )
1561  {
1562  projPath = relativeBasePath;
1563  }
1564 
1565  if ( projPath.isEmpty() )
1566  {
1567  return src;
1568  }
1569 
1570  // if this is a VSIFILE, remove the VSI prefix and append to final result
1571  QString vsiPrefix = qgsVsiPrefix( src );
1572  if ( ! vsiPrefix.isEmpty() )
1573  {
1574  srcPath.remove( 0, vsiPrefix.size() );
1575  }
1576 
1577 #if defined( Q_OS_WIN )
1578  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1579 
1580  srcPath.replace( '\\', '/' );
1581 
1582  if ( srcPath.startsWith( "//" ) )
1583  {
1584  // keep UNC prefix
1585  srcPath = "\\\\" + srcPath.mid( 2 );
1586  }
1587 
1588  projPath.replace( '\\', '/' );
1589  if ( projPath.startsWith( "//" ) )
1590  {
1591  // keep UNC prefix
1592  projPath = "\\\\" + projPath.mid( 2 );
1593  }
1594 #else
1595  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1596 #endif
1597 
1598  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1599  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1600 
1601  // remove project file element
1602  projElems.removeLast();
1603 
1604  projElems.removeAll( "." );
1605  srcElems.removeAll( "." );
1606 
1607  // remove common part
1608  int n = 0;
1609  while ( !srcElems.isEmpty() &&
1610  !projElems.isEmpty() &&
1611  srcElems[0].compare( projElems[0], cs ) == 0 )
1612  {
1613  srcElems.removeFirst();
1614  projElems.removeFirst();
1615  n++;
1616  }
1617 
1618  if ( n == 0 )
1619  {
1620  // no common parts; might not even by a file
1621  return src;
1622  }
1623 
1624  if ( !projElems.isEmpty() )
1625  {
1626  // go up to the common directory
1627  for ( int i = 0; i < projElems.size(); i++ )
1628  {
1629  srcElems.insert( 0, ".." );
1630  }
1631  }
1632  else
1633  {
1634  // let it start with . nevertheless,
1635  // so relative path always start with either ./ or ../
1636  srcElems.insert( 0, "." );
1637  }
1638 
1639  return vsiPrefix + srcElems.join( "/" );
1640 }
1641 
1642 void QgsProject::setError( const QString& errorMessage )
1643 {
1644  mErrorMessage = errorMessage;
1645 }
1646 
1648 {
1649  return mErrorMessage;
1650 }
1651 
1653 {
1654  setError( QString() );
1655 }
1656 
1658 {
1659  delete mBadLayerHandler;
1660  mBadLayerHandler = handler;
1661 }
1662 
1664 {
1665  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1666  if ( it == mEmbeddedLayers.constEnd() )
1667  {
1668  return QString();
1669  }
1670  return it.value().first;
1671 }
1672 
1673 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1674  QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList, bool saveFlag )
1675 {
1676  QgsDebugCall;
1677 
1678  static QString prevProjectFilePath;
1679  static QDateTime prevProjectFileTimestamp;
1680  static QDomDocument projectDocument;
1681 
1682  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1683 
1684  if ( projectFilePath != prevProjectFilePath || projectFileTimestamp != prevProjectFileTimestamp )
1685  {
1686  prevProjectFilePath.clear();
1687 
1688  QFile projectFile( projectFilePath );
1689  if ( !projectFile.open( QIODevice::ReadOnly ) )
1690  {
1691  return false;
1692  }
1693 
1694  if ( !projectDocument.setContent( &projectFile ) )
1695  {
1696  return false;
1697  }
1698 
1699  prevProjectFilePath = projectFilePath;
1700  prevProjectFileTimestamp = projectFileTimestamp;
1701  }
1702 
1703  // does project store pathes absolute or relative?
1704  bool useAbsolutePathes = true;
1705 
1706  QDomElement propertiesElem = projectDocument.documentElement().firstChildElement( "properties" );
1707  if ( !propertiesElem.isNull() )
1708  {
1709  QDomElement absElem = propertiesElem.firstChildElement( "Paths" ).firstChildElement( "Absolute" );
1710  if ( !absElem.isNull() )
1711  {
1712  useAbsolutePathes = absElem.text().compare( "true", Qt::CaseInsensitive ) == 0;
1713  }
1714  }
1715 
1716  QDomElement projectLayersElem = projectDocument.documentElement().firstChildElement( "projectlayers" );
1717  if ( projectLayersElem.isNull() )
1718  {
1719  return false;
1720  }
1721 
1722  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( "maplayer" );
1723  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1724  {
1725  // get layer id
1726  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1727  QString id = mapLayerElem.firstChildElement( "id" ).text();
1728  if ( id == layerId )
1729  {
1730  // layer can be embedded only once
1731  if ( mapLayerElem.attribute( "embedded" ) == "1" )
1732  {
1733  return false;
1734  }
1735 
1736  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1737 
1738  // change datasource path from relative to absolute if necessary
1739  // see also QgsMapLayer::readLayerXML
1740  if ( !useAbsolutePathes )
1741  {
1742  QString provider( mapLayerElem.firstChildElement( "provider" ).text() );
1743  QDomElement dsElem( mapLayerElem.firstChildElement( "datasource" ) );
1744  QString datasource( dsElem.text() );
1745  if ( provider == "spatialite" )
1746  {
1747  QgsDataSourceURI uri( datasource );
1748  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + uri.database() );
1749  if ( absoluteDs.exists() )
1750  {
1751  uri.setDatabase( absoluteDs.absoluteFilePath() );
1752  datasource = uri.uri();
1753  }
1754  }
1755  else if ( provider == "ogr" )
1756  {
1757  QStringList theURIParts( datasource.split( '|' ) );
1758  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1759  if ( absoluteDs.exists() )
1760  {
1761  theURIParts[0] = absoluteDs.absoluteFilePath();
1762  datasource = theURIParts.join( "|" );
1763  }
1764  }
1765  else if ( provider == "gpx" )
1766  {
1767  QStringList theURIParts( datasource.split( '?' ) );
1768  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1769  if ( absoluteDs.exists() )
1770  {
1771  theURIParts[0] = absoluteDs.absoluteFilePath();
1772  datasource = theURIParts.join( "?" );
1773  }
1774  }
1775  else if ( provider == "delimitedtext" )
1776  {
1777  QUrl urlSource( QUrl::fromEncoded( datasource.toAscii() ) );
1778 
1779  if ( !datasource.startsWith( "file:" ) )
1780  {
1781  QUrl file( QUrl::fromLocalFile( datasource.left( datasource.indexOf( '?' ) ) ) );
1782  urlSource.setScheme( "file" );
1783  urlSource.setPath( file.path() );
1784  }
1785 
1786  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + urlSource.toLocalFile() );
1787  if ( absoluteDs.exists() )
1788  {
1789  QUrl urlDest = QUrl::fromLocalFile( absoluteDs.absoluteFilePath() );
1790  urlDest.setQueryItems( urlSource.queryItems() );
1791  datasource = QString::fromAscii( urlDest.toEncoded() );
1792  }
1793  }
1794  else
1795  {
1796  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + datasource );
1797  if ( absoluteDs.exists() )
1798  {
1799  datasource = absoluteDs.absoluteFilePath();
1800  }
1801  }
1802 
1803  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1804  dsElem.appendChild( projectDocument.createTextNode( datasource ) );
1805  }
1806 
1807  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1808  {
1809  return true;
1810  }
1811  else
1812  {
1813  mEmbeddedLayers.remove( layerId );
1814  return false;
1815  }
1816  }
1817  }
1818 
1819  return false;
1820 }
1821 
1822 
1823 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1824 {
1825  // open project file, get layer ids in group, add the layers
1826  QFile projectFile( projectFilePath );
1827  if ( !projectFile.open( QIODevice::ReadOnly ) )
1828  {
1829  return nullptr;
1830  }
1831 
1832  QDomDocument projectDocument;
1833  if ( !projectDocument.setContent( &projectFile ) )
1834  {
1835  return nullptr;
1836  }
1837 
1838  // store identify disabled layers of the embedded project
1839  QSet<QString> embeddedIdentifyDisabledLayers;
1840  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( "properties" ).firstChildElement( "Identify" ).firstChildElement( "disabledLayers" );
1841  if ( !disabledLayersElem.isNull() )
1842  {
1843  QDomNodeList valueList = disabledLayersElem.elementsByTagName( "value" );
1844  for ( int i = 0; i < valueList.size(); ++i )
1845  {
1846  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1847  }
1848  }
1849 
1851 
1852  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( "layer-tree-group" );
1853  if ( !layerTreeElem.isNull() )
1854  {
1855  root->readChildrenFromXML( layerTreeElem );
1856  }
1857  else
1858  {
1859  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( "legend" ) );
1860  }
1861 
1862  QgsLayerTreeGroup *group = root->findGroup( groupName );
1863  if ( !group || group->customProperty( "embedded" ).toBool() )
1864  {
1865  // embedded groups cannot be embedded again
1866  delete root;
1867  return nullptr;
1868  }
1869 
1870  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1871  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1872  delete root;
1873  root = nullptr;
1874 
1875  newGroup->setCustomProperty( "embedded", 1 );
1876  newGroup->setCustomProperty( "embedded_project", projectFilePath );
1877 
1878  // set "embedded" to all children + load embedded layers
1879  mLayerTreeRegistryBridge->setEnabled( false );
1880  initializeEmbeddedSubtree( projectFilePath, newGroup );
1881  mLayerTreeRegistryBridge->setEnabled( true );
1882 
1883  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
1884 
1885  // consider the layers might be identify disabled in its project
1886  Q_FOREACH ( const QString& layerId, newGroup->findLayerIds() )
1887  {
1888  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1889  {
1890  thisProjectIdentifyDisabledLayers.append( layerId );
1891  }
1892 
1893  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1894  if ( layer )
1895  {
1896  layer->setVisible( invisibleLayers.contains( layerId ) ? Qt::Unchecked : Qt::Checked );
1897  }
1898  }
1899 
1900  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
1901 
1902  return newGroup;
1903 }
1904 
1906 {
1907  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1908  {
1909  // all nodes in the subtree will have "embedded" custom property set
1910  child->setCustomProperty( "embedded", 1 );
1911 
1912  if ( QgsLayerTree::isGroup( child ) )
1913  {
1914  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1915  }
1916  else if ( QgsLayerTree::isLayer( child ) )
1917  {
1918  // load the layer into our project
1919  QList<QDomNode> brokenNodes;
1921  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, vectorLayerList, false );
1922  }
1923  }
1924 }
1925 
1926 void QgsProject::setSnapSettingsForLayer( const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
1927 {
1928  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1929  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1930  int idx = layerIdList.indexOf( layerId );
1931  if ( idx != -1 )
1932  {
1933  layerIdList.removeAt( idx );
1934  enabledList.removeAt( idx );
1935  snapTypeList.removeAt( idx );
1936  toleranceUnitList.removeAt( idx );
1937  toleranceList.removeAt( idx );
1938  avoidIntersectionList.removeOne( layerId );
1939  }
1940 
1941  layerIdList.append( layerId );
1942 
1943  // enabled
1944  enabledList.append( enabled ? "enabled" : "disabled" );
1945 
1946  // snap type
1947  QString typeString;
1948  if ( type == QgsSnapper::SnapToSegment )
1949  {
1950  typeString = "to_segment";
1951  }
1952  else if ( type == QgsSnapper::SnapToVertexAndSegment )
1953  {
1954  typeString = "to_vertex_and_segment";
1955  }
1956  else
1957  {
1958  typeString = "to_vertex";
1959  }
1960  snapTypeList.append( typeString );
1961 
1962  // units
1963  toleranceUnitList.append( QString::number( unit ) );
1964 
1965  // tolerance
1966  toleranceList.append( QString::number( tolerance ) );
1967 
1968  // avoid intersection
1969  if ( avoidIntersection )
1970  {
1971  avoidIntersectionList.append( layerId );
1972  }
1973 
1974  writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
1975  writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
1976  writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
1977  writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
1978  writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
1979  writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
1980  emit snapSettingsChanged();
1981 }
1982 
1983 bool QgsProject::snapSettingsForLayer( const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance,
1984  bool &avoidIntersection ) const
1985 {
1986  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1987  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1988  int idx = layerIdList.indexOf( layerId );
1989  if ( idx == -1 )
1990  {
1991  return false;
1992  }
1993 
1994  // make sure all lists are long enough
1995  int minListEntries = idx + 1;
1996  if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
1997  toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
1998  {
1999  return false;
2000  }
2001 
2002  // enabled
2003  enabled = enabledList.at( idx ) == "enabled";
2004 
2005  // snap type
2006  QString snapType = snapTypeList.at( idx );
2007  if ( snapType == "to_segment" )
2008  {
2010  }
2011  else if ( snapType == "to_vertex_and_segment" )
2012  {
2014  }
2015  else // to vertex
2016  {
2017  type = QgsSnapper::SnapToVertex;
2018  }
2019 
2020  // units
2021  if ( toleranceUnitList.at( idx ) == "1" )
2022  {
2023  units = QgsTolerance::Pixels;
2024  }
2025  else if ( toleranceUnitList.at( idx ) == "2" )
2026  {
2028  }
2029  else
2030  {
2031  units = QgsTolerance::LayerUnits;
2032  }
2033 
2034  // tolerance
2035  tolerance = toleranceList.at( idx ).toDouble();
2036 
2037  // avoid intersection
2038  avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
2039 
2040  return true;
2041 }
2042 
2043 void QgsProject::snapSettings( QStringList &layerIdList, QStringList &enabledList, QStringList &snapTypeList, QStringList &toleranceUnitList, QStringList &toleranceList,
2044  QStringList &avoidIntersectionList ) const
2045 {
2046  layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
2047  enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
2048  toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
2049  toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
2050  snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
2051  avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
2052 }
2053 
2055 {
2056  return imp_->evaluateDefaultValues;
2057 }
2058 
2060 {
2061  Q_FOREACH ( QgsMapLayer* layer, QgsMapLayerRegistry::instance()->mapLayers().values() )
2062  {
2063  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( layer );
2064  if ( vl )
2065  {
2067  }
2068  }
2069 
2070  imp_->evaluateDefaultValues = evaluateDefaultValues;
2071 }
2072 
2074 {
2075  QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
2076  emit snapSettingsChanged();
2077 }
2078 
2080 {
2081  return ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) > 0 );
2082 }
2083 
2085 {
2086  QString distanceUnitString = QgsProject::instance()->readEntry( "Measurement", "/DistanceUnits", QString() );
2087  if ( !distanceUnitString.isEmpty() )
2088  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2089 
2090  //fallback to QGIS default measurement unit
2091  QSettings s;
2092  bool ok = false;
2093  QGis::UnitType type = QgsUnitTypes::decodeDistanceUnit( s.value( "/qgis/measure/displayunits" ).toString(), &ok );
2094  return ok ? type : QGis::Meters;
2095 }
2096 
2098 {
2099  QString areaUnitString = QgsProject::instance()->readEntry( "Measurement", "/AreaUnits", QString() );
2100  if ( !areaUnitString.isEmpty() )
2101  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2102 
2103  //fallback to QGIS default area unit
2104  QSettings s;
2105  bool ok = false;
2106  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( "/qgis/measure/areaunits" ).toString(), &ok );
2107  return ok ? type : QgsUnitTypes::SquareMeters;
2108 }
2109 
2111 {
2112  // just ignore any bad layers
2113 }
2114 
2116 {
2117  QFileInfo pfi( fileName() );
2118  if ( !pfi.exists() )
2119  return QString::null;
2120 
2121  return pfi.canonicalPath();
2122 }
2123 
2125 {
2126  return mRelationManager;
2127 }
2128 
2130 {
2131  return mRootGroup;
2132 }
2133 
2135 {
2136  return mVisibilityPresetCollection.data();
2137 }
2138 
2140 {
2141  QStringList currentLayers = nonIdentifiableLayers();
2142 
2143  QStringList newLayers;
2144  Q_FOREACH ( QgsMapLayer* l, layers )
2145  {
2146  newLayers << l->id();
2147  }
2148 
2149  if ( newLayers == currentLayers )
2150  return;
2151 
2152  QStringList disabledLayerIds;
2153 
2154  Q_FOREACH ( QgsMapLayer* l, layers )
2155  {
2156  disabledLayerIds << l->id();
2157  }
2158 
2159  setNonIdentifiableLayers( disabledLayerIds );
2160 }
2161 
2163 {
2164  writeEntry( "Identify", "/disabledLayers", layerIds );
2165 
2166  emit nonIdentifiableLayersChanged( layerIds );
2167 }
2168 
2170 {
2171  return QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" );
2172 }
2173 
2175 {
2176  return imp_->autoTransaction;
2177 }
2178 
2180 {
2181  if ( autoTransaction != imp_->autoTransaction )
2182  {
2183  imp_->autoTransaction = autoTransaction;
2184 
2185  if ( autoTransaction )
2186  onMapLayersAdded( QgsMapLayerRegistry::instance()->mapLayers().values() );
2187  else
2188  cleanTransactionGroups( true );
2189  }
2190 }
2191 
2193 {
2194  return mTransactionGroups;
2195 }
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
bool canConvert(Type t) const
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:410
QString fromAscii(const char *str, int size)
QDomNodeList elementsByTagName(const QString &tagname) const
virtual void handleBadLayers(const QList< QDomNode > &layers, const QDomDocument &projectDom) override
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:405
Base class for all map layer types.
Definition: qgsmaplayer.h:49
void setNonIdentifiableLayers(QList< QgsMapLayer *> layers)
Set a list of layers which should not be taken into account on map identification.
virtual bool seek(qint64 pos)
static void removeInvalidLayers(QgsLayerTreeGroup *group)
Remove layer nodes that refer to invalid layers.
QDomNode item(int index) const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=nullptr) const
void loadingLayer(const QString &)
QgsPropertyKey properties_
Definition: qgsproject.cpp:323
QgsPropertyKey * addKey(const QString &keyName)
add the given property key
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
qint64 pos() const
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
QDomNode appendChild(const QDomNode &newChild)
QDateTime lastRead() const
void push_back(const T &value)
QString attribute(const QString &name, const QString &defValue) const
QgsPropertyValue * setValue(const QString &name, const QVariant &value)
Set the value associated with this key.
QString data() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
static AreaUnit decodeAreaUnit(const QString &string, bool *ok=0)
Decodes an areal unit from a string.
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
QList< QPair< QString, QString > > queryItems() const
Class used to work with layer dependencies stored in a XML project or layer definition file...
void removeFirst()
static QGis::UnitType decodeDistanceUnit(const QString &string, bool *ok=0)
Decodes a distance unit from a string.
void setDatabase(const QString &database)
Set database.
const_iterator constBegin() const
const T & at(int i) const
QString fileName() const
int size() const
void removeAt(int i)
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
void emitVariablesChanged()
Causes the project to emit the variablesChanged() signal.
Definition: qgsproject.cpp:415
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:420
UnitType
Type of unit of tolerance value from settings.
Definition: qgstolerance.h:33
void setFileName(const QString &name)
void push_front(const T &value)
T value() const
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
const_iterator constFind(const Key &key) const
void setSnapSettingsForLayer(const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection)
Convenience function to set snap settings per layer.
QDomElement documentElement() const
QgsLayerTreeGroup * layerTreeRoot() const
Return pointer to the root (invisible) node of the project&#39;s layer tree.
QString join(const QString &separator) const
void setError(const QString &errorMessage)
Set error message from read/write operation.
void clear()
Clear project properties when a new project is started.
Definition: qgsproject.cpp:342
bool exists() const
const_iterator insert(const T &value)
QString & remove(int position, int n)
QgsRelationManager * relationManager() const
const QList< QgsVectorJoinInfo > vectorJoins() const
bool readLayerXML(const QDomElement &layerElement, const QString &relativeBasePath=QString())
Sets state from Dom document.
void projectSaved()
emitted when the project file has been written and closed
QDomNodeList childNodes() const
QStringList nonIdentifiableLayers() const
Get the list of layers which currently should not be taken into account on map identification.
void loadEmbeddedNodes(QgsLayerTreeGroup *group)
Definition: qgsproject.cpp:906
QString tr(const char *sourceText, const char *disambiguation, int n)
virtual void writeXML(QDomElement &parentElement)=0
Write layer tree to XML.
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:579
int size() const
bool isNull() const
void fileNameChanged()
Emitted when the file name of the project changes.
void clear()
QString filePath() const
QDomElement toElement() const
void setPath(const QString &path)
SnappingType
Snap to vertex, to segment or both.
Definition: qgssnapper.h:67
const char * name() const
bool writeEntry(const QString &scope, const QString &key, bool value)
QGis::UnitType distanceUnits() const
Convenience function to query default distance measurement units for project.
QString canonicalFilePath() const
bool isText() const
int count() const
QString number(int n, int base)
int count(const T &value) const
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:746
void append(const T &value)
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
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
QString & insert(int position, QChar ch)
void initializeEmbeddedSubtree(const QString &projectFilePath, QgsLayerTreeGroup *group)
QVariant property(const char *name) const
const_iterator constEnd() const
QString canonicalPath() const
void pop_front()
QString text() const
int toInt(bool *ok) const
Pixels unit of tolerance.
Definition: qgstolerance.h:40
static void _getTitle(const QDomDocument &doc, QString &title)
Get the project title.
Definition: qgsproject.cpp:545
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.
QString database() const
Returns the database.
virtual void clearKeys()
delete any sub-nodes
void setAttribute(const QString &name, const QString &value)
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.
int toInt(bool *ok, int base) const
bool isEmpty() const
QDomNodeList elementsByTagName(const QString &tagname) const
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
void dumpProperties() const
Dump out current project properties to stderr.
QString absoluteFilePath() const
bool isEmpty() const
int removeAll(const T &value)
void readProject(const QDomDocument &)
emitted when project is being read
Listens to the updates in map layer registry and does changes in layer tree.
const char * constData() const
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.
QgsPropertyKey node.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QStringList subkeyList(const QString &scope, const QString &key) const
Return keys with keys – do not return keys that contain only values.
void setScheme(const QString &scheme)
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)
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
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
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:71
qint64 read(char *data, qint64 maxSize)
void setVisible(Qt::CheckState visible)
T & front()
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.
QString uri(bool expandAuthConfig=true) const
return complete uri
T & first()
bool evaluateDefaultValues
Definition: qgsproject.cpp:326
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted, when a layer is being saved.
iterator end()
QDateTime lastModified() const
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
bool isValid()
Return the status of the layer.
An Abstract Base Class for QGIS project property hierarchies.
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
bool hasChildNodes() const
static QString QGIS_VERSION
Definition: qgis.h:46
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() ).
QString toLocalFile() const
QDomText createTextNode(const QString &value)
#define QgsDebugCall
Definition: qgslogger.h:32
iterator end()
const T value(const Key &key) const
bool exists() const
iterator find(const Key &key)
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
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
QDomNode namedItem(const QString &name) const
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers)
Create layer group instance defined in an arbitrary project file.
bool contains(QChar ch, Qt::CaseSensitivity cs) const
void clearError()
Clear error message.
bool topologicalEditing() const
Convenience function to query topological editing status.
QString homePath() const
Return project&#39;s home path.
QString fileName() const
Returns the project&#39;s file name.
virtual void close()
bool contains(const T &value) const
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
uint toTime_t() const
bool isNull() const
Layer unit value.
Definition: qgstolerance.h:38
bool snapSettingsForLayer(const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance, bool &avoidIntersection) const
Convenience function to query snap settings of a layer.
void _getProperties(const QDomDocument &doc, QgsPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:509
QString & replace(int position, int n, QChar after)
bool writeLayerXML(QDomElement &layerElement, QDomDocument &document, const QString &relativeBasePath=QString())
Stores state in Dom node.
bool addLayer(const QDomElement &layerElem, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList)
Creates layer and adds it to maplayer registry.
Definition: qgsproject.cpp:684
QVariant value(const QString &key, const QVariant &defaultValue) const
void setInvalidDataPolicy(InvalidDataPolicy policy)
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:442
QDomNode firstChild() const
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
QString mid(int position, int n) const
QStringList toStringList() const
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 ...
Map (project) units.
Definition: qgstolerance.h:42
QString source() const
Returns the source for the layer.
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:472
QString name() const
every key has a name
void insert(int i, const T &value)
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:188
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name. Searches recursively the whole sub-tree.
QString readPath(QString filename, const QString &relativeBasePath=QString()) const
Turn filename read from the project file to an absolute path.
virtual bool atEnd() const
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:382
QDomElement firstChildElement(const QString &tagName) const
QString qgsVsiPrefix(const QString &path)
Definition: qgis.cpp:342
void setTitle(const QString &title)
Sets the project&#39;s title.
Definition: qgsproject.cpp:391
StandardButton critical(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void entryList(QStringList &entries) const
return keys that do not contain other keys
void removeLast()
bool isWritable() const
bool toBool() const
UnitType
Map units that qgis supports.
Definition: qgis.h:159
bool readXML(QDomNode &keyNode) override
restores property hierarchy to given Dom node
void setQueryItems(const QList< QPair< QString, QString > > &query)
qint64 write(const char *data, qint64 maxSize)
Container class that allows storage of visibility presets consisting of visible map layers and layer ...
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
int indexOf(const QRegExp &rx, int from) const
void prepend(const T &value)
double toDouble(bool *ok) const
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.
Q_DECL_DEPRECATED void clearProperties()
Removes all project properties.
static QgsPluginLayerRegistry * instance()
Means of accessing canonical single instance.
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:399
int size() const
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 providerType() const
Return the provider type for this layer.
void readChildrenFromXML(QDomElement &element, bool looseMatch=false)
Read children from XML and append them to the group.
QDomText toText() const
QgsProperty * findKey_(const QString &scope, const QString &key, QgsPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:113
Type type() const
Default bad layer handler which ignores any missing layers.
Definition: qgsproject.h:557
int size() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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...
QDomDocumentType createDocumentType(const QString &qName, const QString &publicId, const QString &systemId)
QgsVisibilityPresetCollection * visibilityPresetCollection()
Returns pointer to the project&#39;s visibility preset collection.
Represents a vector layer which manages a vector based data sets.
void removeKey_(const QString &scope, const QString &key, QgsPropertyKey &rootProperty)
Definition: qgsproject.cpp:257
int compare(const QString &other) const
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
bool removeOne(const T &value)
virtual bool isValue() const =0
Returns true if is a QgsPropertyValue.
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
AreaUnit
Units of area.
Definition: qgsunittypes.h:49
iterator begin()
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 createJoinCaches()
Caches joined attributes if required (and not already done)
QUrl fromEncoded(const QByteArray &input)
QByteArray toEncoded(QFlags< QUrl::FormattingOption > options) const
void clear()
Clear the project - removes all settings and resets it back to an empty, default state.
Definition: qgsproject.cpp:447
Interface for classes that handle missing layer files when reading project file.
Definition: qgsproject.h:547
QUrl fromLocalFile(const QString &localFile)
Layer tree node points to a map layer.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const
void dump(int tabs=0) const override
Dumps out the keys and values.
const T value(const Key &key) const
QString writePath(const QString &filename, const QString &relativeBasePath=QString()) const
Prepare a filename to save it to the project file.
void snapSettingsChanged()
QByteArray toUtf8() const