QGIS API Documentation  2.15.0-Master (94d88e6)
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  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1138  if ( emIt == mEmbeddedLayers.constEnd() )
1139  {
1140  // general layer metadata
1141  QDomElement maplayerElem = doc->createElement( "maplayer" );
1142 
1143  ml->writeLayerXML( maplayerElem, *doc );
1144 
1145  emit writeMapLayer( ml, maplayerElem, *doc );
1146 
1147  projectLayersNode.appendChild( maplayerElem );
1148  }
1149  else
1150  {
1151  // layer defined in an external project file
1152  // only save embedded layer if not managed by a legend group
1153  if ( emIt.value().second )
1154  {
1155  QDomElement mapLayerElem = doc->createElement( "maplayer" );
1156  mapLayerElem.setAttribute( "embedded", 1 );
1157  mapLayerElem.setAttribute( "project", writePath( emIt.value().first ) );
1158  mapLayerElem.setAttribute( "id", ml->id() );
1159  projectLayersNode.appendChild( mapLayerElem );
1160  }
1161  }
1162  }
1163  li++;
1164  }
1165 
1166  qgisNode.appendChild( projectLayersNode );
1167 
1168  // now add the optional extra properties
1169 
1170  dump_( imp_->properties_ );
1171 
1172  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( imp_->properties_.count() ) ) );
1173 
1174  if ( !imp_->properties_.isEmpty() ) // only worry about properties if we
1175  // actually have any properties
1176  {
1177  imp_->properties_.writeXML( "properties", qgisNode, *doc );
1178  }
1179 
1180  mVisibilityPresetCollection->writeXML( *doc );
1181 
1182  // now wrap it up and ship it to the project file
1183  doc->normalize(); // XXX I'm not entirely sure what this does
1184 
1185  // Create backup file
1186  if ( QFile::exists( fileName() ) )
1187  {
1188  QFile backupFile( fileName() + '~' );
1189  bool ok = true;
1190  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1191  ok &= imp_->file.open( QIODevice::ReadOnly );
1192 
1193  QByteArray ba;
1194  while ( ok && !imp_->file.atEnd() )
1195  {
1196  ba = imp_->file.read( 10240 );
1197  ok &= backupFile.write( ba ) == ba.size();
1198  }
1199 
1200  imp_->file.close();
1201  backupFile.close();
1202 
1203  if ( !ok )
1204  {
1205  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1206  return false;
1207  }
1208 
1209  QFileInfo fi( fileName() );
1210  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1211  utime( backupFile.fileName().toUtf8().constData(), &tb );
1212  }
1213 
1214  if ( !imp_->file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1215  {
1216  imp_->file.close(); // even though we got an error, let's make
1217  // sure it's closed anyway
1218 
1219  setError( tr( "Unable to save to file %1" ).arg( imp_->file.fileName() ) );
1220  return false;
1221  }
1222 
1223  QTemporaryFile tempFile;
1224  bool ok = tempFile.open();
1225  if ( ok )
1226  {
1227  QTextStream projectFileStream( &tempFile );
1228  doc->save( projectFileStream, 2 ); // save as utf-8
1229  ok &= projectFileStream.pos() > -1;
1230 
1231  ok &= tempFile.seek( 0 );
1232 
1233  QByteArray ba;
1234  while ( ok && !tempFile.atEnd() )
1235  {
1236  ba = tempFile.read( 10240 );
1237  ok &= imp_->file.write( ba ) == ba.size();
1238  }
1239 
1240  ok &= imp_->file.error() == QFile::NoError;
1241 
1242  imp_->file.close();
1243  }
1244 
1245  tempFile.close();
1246 
1247  if ( !ok )
1248  {
1249  setError( tr( "Unable to save to file %1. Your project "
1250  "may be corrupted on disk. Try clearing some space on the volume and "
1251  "check file permissions before pressing save again." )
1252  .arg( imp_->file.fileName() ) );
1253  return false;
1254  }
1255 
1256  setDirty( false ); // reset to pristine state
1257 
1258  emit projectSaved();
1259 
1260  return true;
1261 } // QgsProject::write
1262 
1263 
1264 
1266 {
1267  clear();
1268 
1269  setDirty( true );
1270 } // QgsProject::clearProperties()
1271 
1272 
1273 
1274 bool
1275 QgsProject::writeEntry( QString const &scope, const QString &key, bool value )
1276 {
1277  setDirty( true );
1278 
1279  return addKey_( scope, key, &imp_->properties_, value );
1280 } // QgsProject::writeEntry ( ..., bool value )
1281 
1282 
1283 bool
1284 QgsProject::writeEntry( QString const &scope, const QString &key,
1285  double value )
1286 {
1287  setDirty( true );
1288 
1289  return addKey_( scope, key, &imp_->properties_, value );
1290 } // QgsProject::writeEntry ( ..., double value )
1291 
1292 
1293 bool
1294 QgsProject::writeEntry( QString const &scope, const QString &key, int value )
1295 {
1296  setDirty( true );
1297 
1298  return addKey_( scope, key, &imp_->properties_, value );
1299 } // QgsProject::writeEntry ( ..., int value )
1300 
1301 
1302 bool
1303 QgsProject::writeEntry( QString const &scope, const QString &key,
1304  const QString &value )
1305 {
1306  setDirty( true );
1307 
1308  return addKey_( scope, key, &imp_->properties_, value );
1309 } // QgsProject::writeEntry ( ..., const QString &value )
1310 
1311 
1312 bool
1313 QgsProject::writeEntry( QString const &scope, const QString &key,
1314  const QStringList &value )
1315 {
1316  setDirty( true );
1317 
1318  return addKey_( scope, key, &imp_->properties_, value );
1319 } // QgsProject::writeEntry ( ..., const QStringList &value )
1320 
1323  const QString &key,
1324  const QStringList& def,
1325  bool *ok ) const
1326 {
1327  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1328 
1329  QVariant value;
1330 
1331  if ( property )
1332  {
1333  value = property->value();
1334 
1335  bool valid = QVariant::StringList == value.type();
1336  if ( ok )
1337  *ok = valid;
1338 
1339  if ( valid )
1340  {
1341  return value.toStringList();
1342  }
1343  }
1344 
1345  return def;
1346 } // QgsProject::readListEntry
1347 
1348 
1349 QString
1351  const QString &key,
1352  const QString &def,
1353  bool *ok ) const
1354 {
1355  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1356 
1357  QVariant value;
1358 
1359  if ( property )
1360  {
1361  value = property->value();
1362 
1363  bool valid = value.canConvert( QVariant::String );
1364  if ( ok )
1365  *ok = valid;
1366 
1367  if ( valid )
1368  return value.toString();
1369  }
1370 
1371  return def;
1372 } // QgsProject::readEntry
1373 
1374 
1375 int
1376 QgsProject::readNumEntry( QString const &scope, const QString &key, int def,
1377  bool *ok ) const
1378 {
1379  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1380 
1381  QVariant value;
1382 
1383  if ( property )
1384  {
1385  value = property->value();
1386  }
1387 
1388  bool valid = value.canConvert( QVariant::String );
1389 
1390  if ( ok )
1391  {
1392  *ok = valid;
1393  }
1394 
1395  if ( valid )
1396  {
1397  return value.toInt();
1398  }
1399 
1400  return def;
1401 } // QgsProject::readNumEntry
1402 
1403 
1404 double
1405 QgsProject::readDoubleEntry( QString const &scope, const QString &key,
1406  double def,
1407  bool *ok ) const
1408 {
1409  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1410  if ( property )
1411  {
1412  QVariant value = property->value();
1413 
1414  bool valid = value.canConvert( QVariant::Double );
1415  if ( ok )
1416  *ok = valid;
1417 
1418  if ( valid )
1419  return value.toDouble();
1420  }
1421 
1422  return def;
1423 } // QgsProject::readDoubleEntry
1424 
1425 
1426 bool
1427 QgsProject::readBoolEntry( QString const &scope, const QString &key, bool def,
1428  bool *ok ) const
1429 {
1430  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1431 
1432  if ( property )
1433  {
1434  QVariant value = property->value();
1435 
1436  bool valid = value.canConvert( QVariant::Bool );
1437  if ( ok )
1438  *ok = valid;
1439 
1440  if ( valid )
1441  return value.toBool();
1442  }
1443 
1444  return def;
1445 } // QgsProject::readBoolEntry
1446 
1447 
1448 bool QgsProject::removeEntry( QString const &scope, const QString &key )
1449 {
1450  removeKey_( scope, key, imp_->properties_ );
1451 
1452  setDirty( true );
1453 
1454  return !findKey_( scope, key, imp_->properties_ );
1455 } // QgsProject::removeEntry
1456 
1457 
1458 
1459 QStringList QgsProject::entryList( QString const &scope, QString const &key ) const
1460 {
1461  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1462 
1463  QStringList entries;
1464 
1465  if ( foundProperty )
1466  {
1467  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1468 
1469  if ( propertyKey )
1470  { propertyKey->entryList( entries ); }
1471  }
1472 
1473  return entries;
1474 } // QgsProject::entryList
1475 
1476 
1477 QStringList QgsProject::subkeyList( QString const &scope, QString const &key ) const
1478 {
1479  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1480 
1481  QStringList entries;
1482 
1483  if ( foundProperty )
1484  {
1485  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1486 
1487  if ( propertyKey )
1488  { propertyKey->subkeyList( entries ); }
1489  }
1490 
1491  return entries;
1492 
1493 } // QgsProject::subkeyList
1494 
1495 
1496 
1498 {
1499  dump_( imp_->properties_ );
1500 } // QgsProject::dumpProperties
1501 
1502 
1503 // return the absolute path from a filename read from project file
1505 {
1506  if ( readBoolEntry( "Paths", "/Absolute", false ) )
1507  {
1508  return src;
1509  }
1510 
1511  // if this is a VSIFILE, remove the VSI prefix and append to final result
1512  QString vsiPrefix = qgsVsiPrefix( src );
1513  if ( ! vsiPrefix.isEmpty() )
1514  {
1515  // unfortunately qgsVsiPrefix returns prefix also for files like "/x/y/z.gz"
1516  // so we need to check if we really have the prefix
1517  if ( src.startsWith( "/vsi", Qt::CaseInsensitive ) )
1518  src.remove( 0, vsiPrefix.size() );
1519  else
1520  vsiPrefix.clear();
1521  }
1522 
1523  // relative path should always start with ./ or ../
1524  if ( !src.startsWith( "./" ) && !src.startsWith( "../" ) )
1525  {
1526 #if defined(Q_OS_WIN)
1527  if ( src.startsWith( "\\\\" ) ||
1528  src.startsWith( "//" ) ||
1529  ( src[0].isLetter() && src[1] == ':' ) )
1530  {
1531  // UNC or absolute path
1532  return vsiPrefix + src;
1533  }
1534 #else
1535  if ( src[0] == '/' )
1536  {
1537  // absolute path
1538  return vsiPrefix + src;
1539  }
1540 #endif
1541 
1542  // so this one isn't absolute, but also doesn't start // with ./ or ../.
1543  // That means that it was saved with an earlier version of "relative path support",
1544  // where the source file had to exist and only the project directory was stripped
1545  // from the filename.
1546  QString home = homePath();
1547  if ( home.isNull() )
1548  return vsiPrefix + src;
1549 
1550  QFileInfo fi( home + '/' + src );
1551 
1552  if ( !fi.exists() )
1553  {
1554  return vsiPrefix + src;
1555  }
1556  else
1557  {
1558  return vsiPrefix + fi.canonicalFilePath();
1559  }
1560  }
1561 
1562  QString srcPath = src;
1563  QString projPath = fileName();
1564 
1565  if ( projPath.isEmpty() )
1566  {
1567  return vsiPrefix + src;
1568  }
1569 
1570 #if defined(Q_OS_WIN)
1571  srcPath.replace( '\\', '/' );
1572  projPath.replace( '\\', '/' );
1573 
1574  bool uncPath = projPath.startsWith( "//" );
1575 #endif
1576 
1577  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1578  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1579 
1580 #if defined(Q_OS_WIN)
1581  if ( uncPath )
1582  {
1583  projElems.insert( 0, "" );
1584  projElems.insert( 0, "" );
1585  }
1586 #endif
1587 
1588  // remove project file element
1589  projElems.removeLast();
1590 
1591  // append source path elements
1592  projElems << srcElems;
1593  projElems.removeAll( "." );
1594 
1595  // resolve ..
1596  int pos;
1597  while (( pos = projElems.indexOf( ".." ) ) > 0 )
1598  {
1599  // remove preceding element and ..
1600  projElems.removeAt( pos - 1 );
1601  projElems.removeAt( pos - 1 );
1602  }
1603 
1604 #if !defined(Q_OS_WIN)
1605  // make path absolute
1606  projElems.prepend( "" );
1607 #endif
1608 
1609  return vsiPrefix + projElems.join( "/" );
1610 }
1611 
1612 // return the absolute or relative path to write it to the project file
1613 QString QgsProject::writePath( const QString& src, const QString& relativeBasePath ) const
1614 {
1615  if ( readBoolEntry( "Paths", "/Absolute", false ) || src.isEmpty() )
1616  {
1617  return src;
1618  }
1619 
1620  QFileInfo srcFileInfo( src );
1621  QFileInfo projFileInfo( fileName() );
1622  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
1623  QString projPath = projFileInfo.canonicalFilePath();
1624 
1625  if ( !relativeBasePath.isNull() )
1626  {
1627  projPath = relativeBasePath;
1628  }
1629 
1630  if ( projPath.isEmpty() )
1631  {
1632  return src;
1633  }
1634 
1635  // if this is a VSIFILE, remove the VSI prefix and append to final result
1636  QString vsiPrefix = qgsVsiPrefix( src );
1637  if ( ! vsiPrefix.isEmpty() )
1638  {
1639  srcPath.remove( 0, vsiPrefix.size() );
1640  }
1641 
1642 #if defined( Q_OS_WIN )
1643  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1644 
1645  srcPath.replace( '\\', '/' );
1646 
1647  if ( srcPath.startsWith( "//" ) )
1648  {
1649  // keep UNC prefix
1650  srcPath = "\\\\" + srcPath.mid( 2 );
1651  }
1652 
1653  projPath.replace( '\\', '/' );
1654  if ( projPath.startsWith( "//" ) )
1655  {
1656  // keep UNC prefix
1657  projPath = "\\\\" + projPath.mid( 2 );
1658  }
1659 #else
1660  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1661 #endif
1662 
1663  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1664  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1665 
1666  // remove project file element
1667  projElems.removeLast();
1668 
1669  projElems.removeAll( "." );
1670  srcElems.removeAll( "." );
1671 
1672  // remove common part
1673  int n = 0;
1674  while ( !srcElems.isEmpty() &&
1675  !projElems.isEmpty() &&
1676  srcElems[0].compare( projElems[0], cs ) == 0 )
1677  {
1678  srcElems.removeFirst();
1679  projElems.removeFirst();
1680  n++;
1681  }
1682 
1683  if ( n == 0 )
1684  {
1685  // no common parts; might not even by a file
1686  return src;
1687  }
1688 
1689  if ( !projElems.isEmpty() )
1690  {
1691  // go up to the common directory
1692  for ( int i = 0; i < projElems.size(); i++ )
1693  {
1694  srcElems.insert( 0, ".." );
1695  }
1696  }
1697  else
1698  {
1699  // let it start with . nevertheless,
1700  // so relative path always start with either ./ or ../
1701  srcElems.insert( 0, "." );
1702  }
1703 
1704  return vsiPrefix + srcElems.join( "/" );
1705 }
1706 
1707 void QgsProject::setError( const QString& errorMessage )
1708 {
1709  mErrorMessage = errorMessage;
1710 }
1711 
1713 {
1714  return mErrorMessage;
1715 }
1716 
1718 {
1719  setError( QString() );
1720 }
1721 
1723 {
1724  delete mBadLayerHandler;
1725  mBadLayerHandler = handler;
1726 }
1727 
1729 {
1730  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1731  if ( it == mEmbeddedLayers.constEnd() )
1732  {
1733  return QString();
1734  }
1735  return it.value().first;
1736 }
1737 
1738 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1739  QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList, bool saveFlag )
1740 {
1741  QgsDebugCall;
1742 
1743  static QString prevProjectFilePath;
1744  static QDateTime prevProjectFileTimestamp;
1745  static QDomDocument projectDocument;
1746 
1747  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1748 
1749  if ( projectFilePath != prevProjectFilePath || projectFileTimestamp != prevProjectFileTimestamp )
1750  {
1751  prevProjectFilePath.clear();
1752 
1753  QFile projectFile( projectFilePath );
1754  if ( !projectFile.open( QIODevice::ReadOnly ) )
1755  {
1756  return false;
1757  }
1758 
1759  if ( !projectDocument.setContent( &projectFile ) )
1760  {
1761  return false;
1762  }
1763 
1764  prevProjectFilePath = projectFilePath;
1765  prevProjectFileTimestamp = projectFileTimestamp;
1766  }
1767 
1768  // does project store pathes absolute or relative?
1769  bool useAbsolutePathes = true;
1770 
1771  QDomElement propertiesElem = projectDocument.documentElement().firstChildElement( "properties" );
1772  if ( !propertiesElem.isNull() )
1773  {
1774  QDomElement absElem = propertiesElem.firstChildElement( "Paths" ).firstChildElement( "Absolute" );
1775  if ( !absElem.isNull() )
1776  {
1777  useAbsolutePathes = absElem.text().compare( "true", Qt::CaseInsensitive ) == 0;
1778  }
1779  }
1780 
1781  QDomElement projectLayersElem = projectDocument.documentElement().firstChildElement( "projectlayers" );
1782  if ( projectLayersElem.isNull() )
1783  {
1784  return false;
1785  }
1786 
1787  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( "maplayer" );
1788  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1789  {
1790  // get layer id
1791  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1792  QString id = mapLayerElem.firstChildElement( "id" ).text();
1793  if ( id == layerId )
1794  {
1795  // layer can be embedded only once
1796  if ( mapLayerElem.attribute( "embedded" ) == "1" )
1797  {
1798  return false;
1799  }
1800 
1801  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1802 
1803  // change datasource path from relative to absolute if necessary
1804  // see also QgsMapLayer::readLayerXML
1805  if ( !useAbsolutePathes )
1806  {
1807  QString provider( mapLayerElem.firstChildElement( "provider" ).text() );
1808  QDomElement dsElem( mapLayerElem.firstChildElement( "datasource" ) );
1809  QString datasource( dsElem.text() );
1810  if ( provider == "spatialite" )
1811  {
1812  QgsDataSourceURI uri( datasource );
1813  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + uri.database() );
1814  if ( absoluteDs.exists() )
1815  {
1816  uri.setDatabase( absoluteDs.absoluteFilePath() );
1817  datasource = uri.uri();
1818  }
1819  }
1820  else if ( provider == "ogr" )
1821  {
1822  QStringList theURIParts( datasource.split( '|' ) );
1823  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1824  if ( absoluteDs.exists() )
1825  {
1826  theURIParts[0] = absoluteDs.absoluteFilePath();
1827  datasource = theURIParts.join( "|" );
1828  }
1829  }
1830  else if ( provider == "gpx" )
1831  {
1832  QStringList theURIParts( datasource.split( '?' ) );
1833  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1834  if ( absoluteDs.exists() )
1835  {
1836  theURIParts[0] = absoluteDs.absoluteFilePath();
1837  datasource = theURIParts.join( "?" );
1838  }
1839  }
1840  else if ( provider == "delimitedtext" )
1841  {
1842  QUrl urlSource( QUrl::fromEncoded( datasource.toAscii() ) );
1843 
1844  if ( !datasource.startsWith( "file:" ) )
1845  {
1846  QUrl file( QUrl::fromLocalFile( datasource.left( datasource.indexOf( '?' ) ) ) );
1847  urlSource.setScheme( "file" );
1848  urlSource.setPath( file.path() );
1849  }
1850 
1851  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + urlSource.toLocalFile() );
1852  if ( absoluteDs.exists() )
1853  {
1854  QUrl urlDest = QUrl::fromLocalFile( absoluteDs.absoluteFilePath() );
1855  urlDest.setQueryItems( urlSource.queryItems() );
1856  datasource = QString::fromAscii( urlDest.toEncoded() );
1857  }
1858  }
1859  else
1860  {
1861  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + datasource );
1862  if ( absoluteDs.exists() )
1863  {
1864  datasource = absoluteDs.absoluteFilePath();
1865  }
1866  }
1867 
1868  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1869  dsElem.appendChild( projectDocument.createTextNode( datasource ) );
1870  }
1871 
1872  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1873  {
1874  return true;
1875  }
1876  else
1877  {
1878  mEmbeddedLayers.remove( layerId );
1879  return false;
1880  }
1881  }
1882  }
1883 
1884  return false;
1885 }
1886 
1887 
1888 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1889 {
1890  // open project file, get layer ids in group, add the layers
1891  QFile projectFile( projectFilePath );
1892  if ( !projectFile.open( QIODevice::ReadOnly ) )
1893  {
1894  return nullptr;
1895  }
1896 
1897  QDomDocument projectDocument;
1898  if ( !projectDocument.setContent( &projectFile ) )
1899  {
1900  return nullptr;
1901  }
1902 
1903  // store identify disabled layers of the embedded project
1904  QSet<QString> embeddedIdentifyDisabledLayers;
1905  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( "properties" ).firstChildElement( "Identify" ).firstChildElement( "disabledLayers" );
1906  if ( !disabledLayersElem.isNull() )
1907  {
1908  QDomNodeList valueList = disabledLayersElem.elementsByTagName( "value" );
1909  for ( int i = 0; i < valueList.size(); ++i )
1910  {
1911  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1912  }
1913  }
1914 
1916 
1917  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( "layer-tree-group" );
1918  if ( !layerTreeElem.isNull() )
1919  {
1920  root->readChildrenFromXML( layerTreeElem );
1921  }
1922  else
1923  {
1924  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( "legend" ) );
1925  }
1926 
1927  QgsLayerTreeGroup *group = root->findGroup( groupName );
1928  if ( !group || group->customProperty( "embedded" ).toBool() )
1929  {
1930  // embedded groups cannot be embedded again
1931  delete root;
1932  return nullptr;
1933  }
1934 
1935  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1936  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1937  delete root;
1938  root = nullptr;
1939 
1940  newGroup->setCustomProperty( "embedded", 1 );
1941  newGroup->setCustomProperty( "embedded_project", projectFilePath );
1942 
1943  // set "embedded" to all children + load embedded layers
1944  mLayerTreeRegistryBridge->setEnabled( false );
1945  initializeEmbeddedSubtree( projectFilePath, newGroup );
1946  mLayerTreeRegistryBridge->setEnabled( true );
1947 
1948  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
1949 
1950  // consider the layers might be identify disabled in its project
1951  Q_FOREACH ( const QString& layerId, newGroup->findLayerIds() )
1952  {
1953  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1954  {
1955  thisProjectIdentifyDisabledLayers.append( layerId );
1956  }
1957 
1958  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1959  if ( layer )
1960  {
1961  layer->setVisible( invisibleLayers.contains( layerId ) ? Qt::Unchecked : Qt::Checked );
1962  }
1963  }
1964 
1965  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
1966 
1967  return newGroup;
1968 }
1969 
1971 {
1972  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1973  {
1974  // all nodes in the subtree will have "embedded" custom property set
1975  child->setCustomProperty( "embedded", 1 );
1976 
1977  if ( QgsLayerTree::isGroup( child ) )
1978  {
1979  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1980  }
1981  else if ( QgsLayerTree::isLayer( child ) )
1982  {
1983  // load the layer into our project
1984  QList<QDomNode> brokenNodes;
1986  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, vectorLayerList, false );
1987  }
1988  }
1989 }
1990 
1991 void QgsProject::setSnapSettingsForLayer( const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
1992 {
1993  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1994  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1995  int idx = layerIdList.indexOf( layerId );
1996  if ( idx != -1 )
1997  {
1998  layerIdList.removeAt( idx );
1999  enabledList.removeAt( idx );
2000  snapTypeList.removeAt( idx );
2001  toleranceUnitList.removeAt( idx );
2002  toleranceList.removeAt( idx );
2003  avoidIntersectionList.removeOne( layerId );
2004  }
2005 
2006  layerIdList.append( layerId );
2007 
2008  // enabled
2009  enabledList.append( enabled ? "enabled" : "disabled" );
2010 
2011  // snap type
2012  QString typeString;
2013  if ( type == QgsSnapper::SnapToSegment )
2014  {
2015  typeString = "to_segment";
2016  }
2017  else if ( type == QgsSnapper::SnapToVertexAndSegment )
2018  {
2019  typeString = "to_vertex_and_segment";
2020  }
2021  else
2022  {
2023  typeString = "to_vertex";
2024  }
2025  snapTypeList.append( typeString );
2026 
2027  // units
2028  toleranceUnitList.append( QString::number( unit ) );
2029 
2030  // tolerance
2031  toleranceList.append( QString::number( tolerance ) );
2032 
2033  // avoid intersection
2034  if ( avoidIntersection )
2035  {
2036  avoidIntersectionList.append( layerId );
2037  }
2038 
2039  writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
2040  writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
2041  writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
2042  writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
2043  writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
2044  writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
2045  emit snapSettingsChanged();
2046 }
2047 
2048 bool QgsProject::snapSettingsForLayer( const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance,
2049  bool &avoidIntersection ) const
2050 {
2051  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
2052  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
2053  int idx = layerIdList.indexOf( layerId );
2054  if ( idx == -1 )
2055  {
2056  return false;
2057  }
2058 
2059  // make sure all lists are long enough
2060  int minListEntries = idx + 1;
2061  if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
2062  toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
2063  {
2064  return false;
2065  }
2066 
2067  // enabled
2068  enabled = enabledList.at( idx ) == "enabled";
2069 
2070  // snap type
2071  QString snapType = snapTypeList.at( idx );
2072  if ( snapType == "to_segment" )
2073  {
2075  }
2076  else if ( snapType == "to_vertex_and_segment" )
2077  {
2079  }
2080  else // to vertex
2081  {
2082  type = QgsSnapper::SnapToVertex;
2083  }
2084 
2085  // units
2086  if ( toleranceUnitList.at( idx ) == "1" )
2087  {
2088  units = QgsTolerance::Pixels;
2089  }
2090  else if ( toleranceUnitList.at( idx ) == "2" )
2091  {
2093  }
2094  else
2095  {
2096  units = QgsTolerance::LayerUnits;
2097  }
2098 
2099  // tolerance
2100  tolerance = toleranceList.at( idx ).toDouble();
2101 
2102  // avoid intersection
2103  avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
2104 
2105  return true;
2106 }
2107 
2108 void QgsProject::snapSettings( QStringList &layerIdList, QStringList &enabledList, QStringList &snapTypeList, QStringList &toleranceUnitList, QStringList &toleranceList,
2109  QStringList &avoidIntersectionList ) const
2110 {
2111  layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
2112  enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
2113  toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
2114  toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
2115  snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
2116  avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
2117 }
2118 
2120 {
2121  QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
2122  emit snapSettingsChanged();
2123 }
2124 
2126 {
2127  return ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) > 0 );
2128 }
2129 
2131 {
2132  QString distanceUnitString = QgsProject::instance()->readEntry( "Measurement", "/DistanceUnits", QString() );
2133  if ( !distanceUnitString.isEmpty() )
2134  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2135 
2136  //fallback to QGIS default measurement unit
2137  QSettings s;
2138  bool ok = false;
2139  QGis::UnitType type = QgsUnitTypes::decodeDistanceUnit( s.value( "/qgis/measure/displayunits" ).toString(), &ok );
2140  return ok ? type : QGis::Meters;
2141 }
2142 
2144 {
2145  QString areaUnitString = QgsProject::instance()->readEntry( "Measurement", "/AreaUnits", QString() );
2146  if ( !areaUnitString.isEmpty() )
2147  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2148 
2149  //fallback to QGIS default area unit
2150  QSettings s;
2151  bool ok = false;
2152  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( "/qgis/measure/areaunits" ).toString(), &ok );
2153  return ok ? type : QgsUnitTypes::SquareMeters;
2154 }
2155 
2157 {
2158  // just ignore any bad layers
2159 }
2160 
2162 {
2163  QFileInfo pfi( fileName() );
2164  if ( !pfi.exists() )
2165  return QString::null;
2166 
2167  return pfi.canonicalPath();
2168 }
2169 
2171 {
2172  return mRelationManager;
2173 }
2174 
2176 {
2177  return mRootGroup;
2178 }
2179 
2181 {
2182  return mVisibilityPresetCollection.data();
2183 }
2184 
2186 {
2187  QStringList currentLayers = nonIdentifiableLayers();
2188 
2189  QStringList newLayers;
2190  Q_FOREACH ( QgsMapLayer* l, layers )
2191  {
2192  newLayers << l->id();
2193  }
2194 
2195  if ( newLayers == currentLayers )
2196  return;
2197 
2198  QStringList disabledLayerIds;
2199 
2200  Q_FOREACH ( QgsMapLayer* l, layers )
2201  {
2202  disabledLayerIds << l->id();
2203  }
2204 
2205  setNonIdentifiableLayers( disabledLayerIds );
2206 }
2207 
2209 {
2210  writeEntry( "Identify", "/disabledLayers", layerIds );
2211 
2212  emit nonIdentifiableLayersChanged( layerIds );
2213 }
2214 
2216 {
2217  return QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" );
2218 }
2219 
2221 {
2222  return imp_->autoTransaction;
2223 }
2224 
2226 {
2227  if ( autoTransaction != imp_->autoTransaction )
2228  {
2229  imp_->autoTransaction = autoTransaction;
2230 
2231  if ( autoTransaction )
2232  addToTransactionGroups( QgsMapLayerRegistry::instance()->mapLayers().values() );
2233  else
2234  cleanTransactionGroups( true );
2235  }
2236 }
2237 
2239 {
2240  return mTransactionGroups;
2241 }
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
static const char * QGIS_VERSION
Definition: qgis.h:46
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: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 ...
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