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