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