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