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