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