QGIS API Documentation  2.17.0-Master (f49f7ce)
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 
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 ( projPath.isEmpty() )
1497  {
1498  return vsiPrefix + src;
1499  }
1500 
1501 #if defined(Q_OS_WIN)
1502  srcPath.replace( '\\', '/' );
1503  projPath.replace( '\\', '/' );
1504 
1505  bool uncPath = projPath.startsWith( "//" );
1506 #endif
1507 
1508  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1509  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1510 
1511 #if defined(Q_OS_WIN)
1512  if ( uncPath )
1513  {
1514  projElems.insert( 0, "" );
1515  projElems.insert( 0, "" );
1516  }
1517 #endif
1518 
1519  // remove project file element
1520  projElems.removeLast();
1521 
1522  // append source path elements
1523  projElems << srcElems;
1524  projElems.removeAll( "." );
1525 
1526  // resolve ..
1527  int pos;
1528  while (( pos = projElems.indexOf( ".." ) ) > 0 )
1529  {
1530  // remove preceding element and ..
1531  projElems.removeAt( pos - 1 );
1532  projElems.removeAt( pos - 1 );
1533  }
1534 
1535 #if !defined(Q_OS_WIN)
1536  // make path absolute
1537  projElems.prepend( "" );
1538 #endif
1539 
1540  return vsiPrefix + projElems.join( "/" );
1541 }
1542 
1543 QString QgsProject::writePath( const QString& src, const QString& relativeBasePath ) const
1544 {
1545  if ( readBoolEntry( "Paths", "/Absolute", false ) || src.isEmpty() )
1546  {
1547  return src;
1548  }
1549 
1550  QFileInfo srcFileInfo( src );
1551  QFileInfo projFileInfo( fileName() );
1552  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
1553  QString projPath = projFileInfo.canonicalFilePath();
1554 
1555  if ( !relativeBasePath.isNull() )
1556  {
1557  projPath = relativeBasePath;
1558  }
1559 
1560  if ( projPath.isEmpty() )
1561  {
1562  return src;
1563  }
1564 
1565  // if this is a VSIFILE, remove the VSI prefix and append to final result
1566  QString vsiPrefix = qgsVsiPrefix( src );
1567  if ( ! vsiPrefix.isEmpty() )
1568  {
1569  srcPath.remove( 0, vsiPrefix.size() );
1570  }
1571 
1572 #if defined( Q_OS_WIN )
1573  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1574 
1575  srcPath.replace( '\\', '/' );
1576 
1577  if ( srcPath.startsWith( "//" ) )
1578  {
1579  // keep UNC prefix
1580  srcPath = "\\\\" + srcPath.mid( 2 );
1581  }
1582 
1583  projPath.replace( '\\', '/' );
1584  if ( projPath.startsWith( "//" ) )
1585  {
1586  // keep UNC prefix
1587  projPath = "\\\\" + projPath.mid( 2 );
1588  }
1589 #else
1590  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1591 #endif
1592 
1593  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1594  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1595 
1596  // remove project file element
1597  projElems.removeLast();
1598 
1599  projElems.removeAll( "." );
1600  srcElems.removeAll( "." );
1601 
1602  // remove common part
1603  int n = 0;
1604  while ( !srcElems.isEmpty() &&
1605  !projElems.isEmpty() &&
1606  srcElems[0].compare( projElems[0], cs ) == 0 )
1607  {
1608  srcElems.removeFirst();
1609  projElems.removeFirst();
1610  n++;
1611  }
1612 
1613  if ( n == 0 )
1614  {
1615  // no common parts; might not even by a file
1616  return src;
1617  }
1618 
1619  if ( !projElems.isEmpty() )
1620  {
1621  // go up to the common directory
1622  for ( int i = 0; i < projElems.size(); i++ )
1623  {
1624  srcElems.insert( 0, ".." );
1625  }
1626  }
1627  else
1628  {
1629  // let it start with . nevertheless,
1630  // so relative path always start with either ./ or ../
1631  srcElems.insert( 0, "." );
1632  }
1633 
1634  return vsiPrefix + srcElems.join( "/" );
1635 }
1636 
1637 void QgsProject::setError( const QString& errorMessage )
1638 {
1639  mErrorMessage = errorMessage;
1640 }
1641 
1643 {
1644  return mErrorMessage;
1645 }
1646 
1648 {
1649  setError( QString() );
1650 }
1651 
1653 {
1654  delete mBadLayerHandler;
1655  mBadLayerHandler = handler;
1656 }
1657 
1659 {
1660  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1661  if ( it == mEmbeddedLayers.constEnd() )
1662  {
1663  return QString();
1664  }
1665  return it.value().first;
1666 }
1667 
1668 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1669  QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList, bool saveFlag )
1670 {
1671  QgsDebugCall;
1672 
1673  static QString prevProjectFilePath;
1674  static QDateTime prevProjectFileTimestamp;
1675  static QDomDocument projectDocument;
1676 
1677  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1678 
1679  if ( projectFilePath != prevProjectFilePath || projectFileTimestamp != prevProjectFileTimestamp )
1680  {
1681  prevProjectFilePath.clear();
1682 
1683  QFile projectFile( projectFilePath );
1684  if ( !projectFile.open( QIODevice::ReadOnly ) )
1685  {
1686  return false;
1687  }
1688 
1689  if ( !projectDocument.setContent( &projectFile ) )
1690  {
1691  return false;
1692  }
1693 
1694  prevProjectFilePath = projectFilePath;
1695  prevProjectFileTimestamp = projectFileTimestamp;
1696  }
1697 
1698  // does project store pathes absolute or relative?
1699  bool useAbsolutePathes = true;
1700 
1701  QDomElement propertiesElem = projectDocument.documentElement().firstChildElement( "properties" );
1702  if ( !propertiesElem.isNull() )
1703  {
1704  QDomElement absElem = propertiesElem.firstChildElement( "Paths" ).firstChildElement( "Absolute" );
1705  if ( !absElem.isNull() )
1706  {
1707  useAbsolutePathes = absElem.text().compare( "true", Qt::CaseInsensitive ) == 0;
1708  }
1709  }
1710 
1711  QDomElement projectLayersElem = projectDocument.documentElement().firstChildElement( "projectlayers" );
1712  if ( projectLayersElem.isNull() )
1713  {
1714  return false;
1715  }
1716 
1717  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( "maplayer" );
1718  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1719  {
1720  // get layer id
1721  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1722  QString id = mapLayerElem.firstChildElement( "id" ).text();
1723  if ( id == layerId )
1724  {
1725  // layer can be embedded only once
1726  if ( mapLayerElem.attribute( "embedded" ) == "1" )
1727  {
1728  return false;
1729  }
1730 
1731  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1732 
1733  // change datasource path from relative to absolute if necessary
1734  // see also QgsMapLayer::readLayerXML
1735  if ( !useAbsolutePathes )
1736  {
1737  QString provider( mapLayerElem.firstChildElement( "provider" ).text() );
1738  QDomElement dsElem( mapLayerElem.firstChildElement( "datasource" ) );
1739  QString datasource( dsElem.text() );
1740  if ( provider == "spatialite" )
1741  {
1742  QgsDataSourceURI uri( datasource );
1743  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + uri.database() );
1744  if ( absoluteDs.exists() )
1745  {
1746  uri.setDatabase( absoluteDs.absoluteFilePath() );
1747  datasource = uri.uri();
1748  }
1749  }
1750  else if ( provider == "ogr" )
1751  {
1752  QStringList theURIParts( datasource.split( '|' ) );
1753  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1754  if ( absoluteDs.exists() )
1755  {
1756  theURIParts[0] = absoluteDs.absoluteFilePath();
1757  datasource = theURIParts.join( "|" );
1758  }
1759  }
1760  else if ( provider == "gpx" )
1761  {
1762  QStringList theURIParts( datasource.split( '?' ) );
1763  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1764  if ( absoluteDs.exists() )
1765  {
1766  theURIParts[0] = absoluteDs.absoluteFilePath();
1767  datasource = theURIParts.join( "?" );
1768  }
1769  }
1770  else if ( provider == "delimitedtext" )
1771  {
1772  QUrl urlSource( QUrl::fromEncoded( datasource.toAscii() ) );
1773 
1774  if ( !datasource.startsWith( "file:" ) )
1775  {
1776  QUrl file( QUrl::fromLocalFile( datasource.left( datasource.indexOf( '?' ) ) ) );
1777  urlSource.setScheme( "file" );
1778  urlSource.setPath( file.path() );
1779  }
1780 
1781  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + urlSource.toLocalFile() );
1782  if ( absoluteDs.exists() )
1783  {
1784  QUrl urlDest = QUrl::fromLocalFile( absoluteDs.absoluteFilePath() );
1785  urlDest.setQueryItems( urlSource.queryItems() );
1786  datasource = QString::fromAscii( urlDest.toEncoded() );
1787  }
1788  }
1789  else
1790  {
1791  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + datasource );
1792  if ( absoluteDs.exists() )
1793  {
1794  datasource = absoluteDs.absoluteFilePath();
1795  }
1796  }
1797 
1798  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1799  dsElem.appendChild( projectDocument.createTextNode( datasource ) );
1800  }
1801 
1802  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1803  {
1804  return true;
1805  }
1806  else
1807  {
1808  mEmbeddedLayers.remove( layerId );
1809  return false;
1810  }
1811  }
1812  }
1813 
1814  return false;
1815 }
1816 
1817 
1818 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1819 {
1820  // open project file, get layer ids in group, add the layers
1821  QFile projectFile( projectFilePath );
1822  if ( !projectFile.open( QIODevice::ReadOnly ) )
1823  {
1824  return nullptr;
1825  }
1826 
1827  QDomDocument projectDocument;
1828  if ( !projectDocument.setContent( &projectFile ) )
1829  {
1830  return nullptr;
1831  }
1832 
1833  // store identify disabled layers of the embedded project
1834  QSet<QString> embeddedIdentifyDisabledLayers;
1835  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( "properties" ).firstChildElement( "Identify" ).firstChildElement( "disabledLayers" );
1836  if ( !disabledLayersElem.isNull() )
1837  {
1838  QDomNodeList valueList = disabledLayersElem.elementsByTagName( "value" );
1839  for ( int i = 0; i < valueList.size(); ++i )
1840  {
1841  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1842  }
1843  }
1844 
1846 
1847  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( "layer-tree-group" );
1848  if ( !layerTreeElem.isNull() )
1849  {
1850  root->readChildrenFromXML( layerTreeElem );
1851  }
1852  else
1853  {
1854  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( "legend" ) );
1855  }
1856 
1857  QgsLayerTreeGroup *group = root->findGroup( groupName );
1858  if ( !group || group->customProperty( "embedded" ).toBool() )
1859  {
1860  // embedded groups cannot be embedded again
1861  delete root;
1862  return nullptr;
1863  }
1864 
1865  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1866  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1867  delete root;
1868  root = nullptr;
1869 
1870  newGroup->setCustomProperty( "embedded", 1 );
1871  newGroup->setCustomProperty( "embedded_project", projectFilePath );
1872 
1873  // set "embedded" to all children + load embedded layers
1874  mLayerTreeRegistryBridge->setEnabled( false );
1875  initializeEmbeddedSubtree( projectFilePath, newGroup );
1876  mLayerTreeRegistryBridge->setEnabled( true );
1877 
1878  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
1879 
1880  // consider the layers might be identify disabled in its project
1881  Q_FOREACH ( const QString& layerId, newGroup->findLayerIds() )
1882  {
1883  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1884  {
1885  thisProjectIdentifyDisabledLayers.append( layerId );
1886  }
1887 
1888  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1889  if ( layer )
1890  {
1891  layer->setVisible( invisibleLayers.contains( layerId ) ? Qt::Unchecked : Qt::Checked );
1892  }
1893  }
1894 
1895  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
1896 
1897  return newGroup;
1898 }
1899 
1901 {
1902  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1903  {
1904  // all nodes in the subtree will have "embedded" custom property set
1905  child->setCustomProperty( "embedded", 1 );
1906 
1907  if ( QgsLayerTree::isGroup( child ) )
1908  {
1909  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1910  }
1911  else if ( QgsLayerTree::isLayer( child ) )
1912  {
1913  // load the layer into our project
1914  QList<QDomNode> brokenNodes;
1916  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, vectorLayerList, false );
1917  }
1918  }
1919 }
1920 
1921 void QgsProject::setSnapSettingsForLayer( const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
1922 {
1923  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1924  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1925  int idx = layerIdList.indexOf( layerId );
1926  if ( idx != -1 )
1927  {
1928  layerIdList.removeAt( idx );
1929  enabledList.removeAt( idx );
1930  snapTypeList.removeAt( idx );
1931  toleranceUnitList.removeAt( idx );
1932  toleranceList.removeAt( idx );
1933  avoidIntersectionList.removeOne( layerId );
1934  }
1935 
1936  layerIdList.append( layerId );
1937 
1938  // enabled
1939  enabledList.append( enabled ? "enabled" : "disabled" );
1940 
1941  // snap type
1942  QString typeString;
1943  if ( type == QgsSnapper::SnapToSegment )
1944  {
1945  typeString = "to_segment";
1946  }
1947  else if ( type == QgsSnapper::SnapToVertexAndSegment )
1948  {
1949  typeString = "to_vertex_and_segment";
1950  }
1951  else
1952  {
1953  typeString = "to_vertex";
1954  }
1955  snapTypeList.append( typeString );
1956 
1957  // units
1958  toleranceUnitList.append( QString::number( unit ) );
1959 
1960  // tolerance
1961  toleranceList.append( QString::number( tolerance ) );
1962 
1963  // avoid intersection
1964  if ( avoidIntersection )
1965  {
1966  avoidIntersectionList.append( layerId );
1967  }
1968 
1969  writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
1970  writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
1971  writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
1972  writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
1973  writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
1974  writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
1975  emit snapSettingsChanged();
1976 }
1977 
1978 bool QgsProject::snapSettingsForLayer( const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance,
1979  bool &avoidIntersection ) const
1980 {
1981  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1982  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1983  int idx = layerIdList.indexOf( layerId );
1984  if ( idx == -1 )
1985  {
1986  return false;
1987  }
1988 
1989  // make sure all lists are long enough
1990  int minListEntries = idx + 1;
1991  if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
1992  toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
1993  {
1994  return false;
1995  }
1996 
1997  // enabled
1998  enabled = enabledList.at( idx ) == "enabled";
1999 
2000  // snap type
2001  QString snapType = snapTypeList.at( idx );
2002  if ( snapType == "to_segment" )
2003  {
2005  }
2006  else if ( snapType == "to_vertex_and_segment" )
2007  {
2009  }
2010  else // to vertex
2011  {
2012  type = QgsSnapper::SnapToVertex;
2013  }
2014 
2015  // units
2016  if ( toleranceUnitList.at( idx ) == "1" )
2017  {
2018  units = QgsTolerance::Pixels;
2019  }
2020  else if ( toleranceUnitList.at( idx ) == "2" )
2021  {
2023  }
2024  else
2025  {
2026  units = QgsTolerance::LayerUnits;
2027  }
2028 
2029  // tolerance
2030  tolerance = toleranceList.at( idx ).toDouble();
2031 
2032  // avoid intersection
2033  avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
2034 
2035  return true;
2036 }
2037 
2038 void QgsProject::snapSettings( QStringList &layerIdList, QStringList &enabledList, QStringList &snapTypeList, QStringList &toleranceUnitList, QStringList &toleranceList,
2039  QStringList &avoidIntersectionList ) const
2040 {
2041  layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
2042  enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
2043  toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
2044  toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
2045  snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
2046  avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
2047 }
2048 
2050 {
2051  return imp_->evaluateDefaultValues;
2052 }
2053 
2055 {
2056  Q_FOREACH ( QgsMapLayer* layer, QgsMapLayerRegistry::instance()->mapLayers().values() )
2057  {
2058  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( layer );
2059  if ( vl )
2060  {
2062  }
2063  }
2064 
2065  imp_->evaluateDefaultValues = evaluateDefaultValues;
2066 }
2067 
2069 {
2070  QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
2071  emit snapSettingsChanged();
2072 }
2073 
2075 {
2076  return ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) > 0 );
2077 }
2078 
2080 {
2081  QString distanceUnitString = QgsProject::instance()->readEntry( "Measurement", "/DistanceUnits", QString() );
2082  if ( !distanceUnitString.isEmpty() )
2083  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2084 
2085  //fallback to QGIS default measurement unit
2086  QSettings s;
2087  bool ok = false;
2088  QGis::UnitType type = QgsUnitTypes::decodeDistanceUnit( s.value( "/qgis/measure/displayunits" ).toString(), &ok );
2089  return ok ? type : QGis::Meters;
2090 }
2091 
2093 {
2094  QString areaUnitString = QgsProject::instance()->readEntry( "Measurement", "/AreaUnits", QString() );
2095  if ( !areaUnitString.isEmpty() )
2096  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2097 
2098  //fallback to QGIS default area unit
2099  QSettings s;
2100  bool ok = false;
2101  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( "/qgis/measure/areaunits" ).toString(), &ok );
2102  return ok ? type : QgsUnitTypes::SquareMeters;
2103 }
2104 
2106 {
2107  // just ignore any bad layers
2108 }
2109 
2111 {
2112  QFileInfo pfi( fileName() );
2113  if ( !pfi.exists() )
2114  return QString::null;
2115 
2116  return pfi.canonicalPath();
2117 }
2118 
2120 {
2121  return mRelationManager;
2122 }
2123 
2125 {
2126  return mRootGroup;
2127 }
2128 
2130 {
2131  return mVisibilityPresetCollection.data();
2132 }
2133 
2135 {
2136  QStringList currentLayers = nonIdentifiableLayers();
2137 
2138  QStringList newLayers;
2139  Q_FOREACH ( QgsMapLayer* l, layers )
2140  {
2141  newLayers << l->id();
2142  }
2143 
2144  if ( newLayers == currentLayers )
2145  return;
2146 
2147  QStringList disabledLayerIds;
2148 
2149  Q_FOREACH ( QgsMapLayer* l, layers )
2150  {
2151  disabledLayerIds << l->id();
2152  }
2153 
2154  setNonIdentifiableLayers( disabledLayerIds );
2155 }
2156 
2158 {
2159  writeEntry( "Identify", "/disabledLayers", layerIds );
2160 
2161  emit nonIdentifiableLayersChanged( layerIds );
2162 }
2163 
2165 {
2166  return QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" );
2167 }
2168 
2170 {
2171  return imp_->autoTransaction;
2172 }
2173 
2175 {
2176  if ( autoTransaction != imp_->autoTransaction )
2177  {
2178  imp_->autoTransaction = autoTransaction;
2179 
2180  if ( autoTransaction )
2181  onMapLayersAdded( QgsMapLayerRegistry::instance()->mapLayers().values() );
2182  else
2183  cleanTransactionGroups( true );
2184  }
2185 }
2186 
2188 {
2189  return mTransactionGroups;
2190 }
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
bool topologicalEditing() const
Convenience function to query topological editing status.
QString fromAscii(const char *str, int size)
QDomNodeList elementsByTagName(const QString &tagname) const
QString database() const
Returns the database.
virtual void handleBadLayers(const QList< QDomNode > &layers, const QDomDocument &projectDom) override
Base class for all map layer types.
Definition: qgsmaplayer.h:49
virtual bool seek(qint64 pos)
static void removeInvalidLayers(QgsLayerTreeGroup *group)
Remove layer nodes that refer to invalid layers.
QString writePath(const QString &filename, const QString &relativeBasePath=QString::null) const
Prepare a filename to save it to the project file.
const QList< QgsVectorJoinInfo > vectorJoins() const
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:399
QDomNode item(int index) const
void readChildrenFromXML(QDomElement &element)
Read children from XML and append them to the group.
void loadingLayer(const QString &)
bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
QgsPropertyKey properties_
Definition: qgsproject.cpp:323
QgsPropertyKey * addKey(const QString &keyName)
add the given property key
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
QGis::UnitType distanceUnits() const
Convenience function to query default distance measurement units for project.
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
qint64 pos() const
QDomNode appendChild(const QDomNode &newChild)
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.
QDateTime lastRead() const
void push_back(const T &value)
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &theMapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
void entryList(QStringList &entries) const
return keys that do not contain other keys
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
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
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
QString source() const
Returns the source for the layer.
const_iterator constFind(const Key &key) const
QString layerIsEmbedded(const QString &id) const
Returns project file path if layer is embedded from other project file.
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
QString join(const QString &separator) const
void setError(const QString &errorMessage)
Set error message from read/write operation.
QString homePath() const
Return project&#39;s home path.
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)
void projectSaved()
emitted when the project file has been written and closed
QDomNodeList childNodes() const
bool writeLayerXML(QDomElement &layerElement, QDomDocument &document, const QString &relativeBasePath=QString::null)
Stores state in Dom node.
void loadEmbeddedNodes(QgsLayerTreeGroup *group)
Definition: qgsproject.cpp:906
QString tr(const char *sourceText, const char *disambiguation, int n)
QString readPath(QString filename) const
Turn filename read from the project file to an absolute path.
virtual void writeXML(QDomElement &parentElement)=0
Write layer tree to XML.
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
QStringList nonIdentifiableLayers() const
Get the list of layers which currently should not be taken into account on map identification.
int size() const
bool isNull() const
void setNonIdentifiableLayers(QList< QgsMapLayer * > layers)
Set a list of layers which should not be taken into account on map identification.
void fileNameChanged()
Emitted when the file name of the project changes.
QString name() const
every key has a name
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
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
const char * name() const
bool writeEntry(const QString &scope, const QString &key, bool value)
QString canonicalFilePath() const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=nullptr) 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)
void removeKey(const QString &keyName)
remove the given key
QString & insert(int position, QChar ch)
QStringList subkeyList(const QString &scope, const QString &key) const
Return keys with keys – do not return keys that contain only values.
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
virtual void clearKeys()
delete any sub-nodes
void setAttribute(const QString &name, const QString &value)
A class to describe the version of a project.
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.
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.
QgsPropertyKey node.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
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
QString fileName() const
Returns the project&#39;s file name.
This class is a base class for nodes in a layer tree.
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
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()
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if a the provider of a give layer supports transactions.
T & first()
bool evaluateDefaultValues
Definition: qgsproject.cpp:326
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted, when a layer is being saved.
QString error() const
Return error message from previous read/write.
QString name() const
Get group&#39;s name.
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)
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
bool hasChildNodes() const
static QString QGIS_VERSION
Definition: qgis.h:46
void setProviderProperty(ProviderProperty property, const QVariant &value)
Allows setting arbitrary properties on the provider.
QgsProperty * find(QString &propertyName)
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.
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.
void subkeyList(QStringList &entries) const
return keys that contain other keys
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.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
void clearError()
Clear error message.
virtual void close()
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:405
bool contains(const T &value) const
uint toTime_t() const
bool isNull() const
Layer unit value.
Definition: qgstolerance.h:38
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
void _getProperties(const QDomDocument &doc, QgsPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:509
QString providerType() const
Return the provider type for this layer.
QString & replace(int position, int n, QChar after)
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.
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
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
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
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.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes. Searches recursively the whole sub-tree.
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.
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 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
QStringList entryList(const QString &scope, const QString &key) const
Return keys with values – do not return keys that contain other keys.
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
void setQueryItems(const QList< QPair< QString, QString > > &query)
qint64 write(const char *data, qint64 maxSize)
bool readLayerXML(const QDomElement &layerElement)
Sets state from Dom document.
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.
int indexOf(const QRegExp &rx, int from) const
void prepend(const T &value)
double toDouble(bool *ok) const
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
void dumpProperties() const
Dump out current project properties to stderr.
Q_DECL_DEPRECATED void clearProperties()
Removes all project properties.
static QgsPluginLayerRegistry * instance()
Means of accessing canonical single instance.
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. ...
QFileInfo fileInfo() const
Returns QFileInfo object for the project&#39;s associated file.
Definition: qgsproject.cpp:442
QgsLayerTreeGroup * layerTreeRoot() const
Return pointer to the root (invisible) node of the project&#39;s layer tree.
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
QString uri(bool expandAuthConfig=true) const
return complete uri
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
QgsRelationManager * relationManager() 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()
bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
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
void snapSettingsChanged()
QByteArray toUtf8() const