QGIS API Documentation  2.15.0-Master (5f66276)
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  imp_->file.setFileName( name );
418 
419  setDirty( true );
420 }
421 
423 {
424  return imp_->file.fileName();
425 }
426 
428 {
429  return QFileInfo( imp_->file );
430 }
431 
433 {
434  imp_->clear();
435  mEmbeddedLayers.clear();
436  mRelationManager->clear();
437 
438  mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
439 
440  mRootGroup->removeAllChildren();
441 
442  // reset some default project properties
443  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
444  writeEntry( "PositionPrecision", "/Automatic", true );
445  writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
446  writeEntry( "Paths", "/Absolute", false );
447 
448  //copy default units to project
449  QSettings s;
450  writeEntry( "Measurement", "/DistanceUnits", s.value( "/qgis/measure/displayunits" ).toString() );
451  writeEntry( "Measurement", "/AreaUnits", s.value( "/qgis/measure/areaunits" ).toString() );
452 
453  setDirty( false );
454 }
455 
456 // basically a debugging tool to dump property list values
457 void dump_( const QgsPropertyKey& topQgsPropertyKey )
458 {
459  QgsDebugMsg( "current properties:" );
460  topQgsPropertyKey.dump();
461 }
462 
463 
494 void _getProperties( const QDomDocument& doc, QgsPropertyKey& project_properties )
495 {
496  QDomNodeList properties = doc.elementsByTagName( "properties" );
497 
498  if ( properties.count() > 1 )
499  {
500  QgsDebugMsg( "there appears to be more than one ``properties'' XML tag ... bailing" );
501  return;
502  }
503  else if ( properties.count() < 1 ) // no properties found, so we're done
504  {
505  return;
506  }
507 
508  // item(0) because there should only be ONE "properties" node
509  QDomNodeList scopes = properties.item( 0 ).childNodes();
510 
511  if ( scopes.count() < 1 )
512  {
513  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
514  return;
515  }
516 
517  QDomNode propertyNode = properties.item( 0 );
518 
519  if ( ! project_properties.readXML( propertyNode ) )
520  {
521  QgsDebugMsg( "Project_properties.readXML() failed" );
522  }
523 }
524 
525 
530 static void _getTitle( const QDomDocument& doc, QString& title )
531 {
532  QDomNodeList nl = doc.elementsByTagName( "title" );
533 
534  title = ""; // by default the title will be empty
535 
536  if ( !nl.count() )
537  {
538  QgsDebugMsg( "unable to find title element" );
539  return;
540  }
541 
542  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element ok
543 
544  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
545  {
546  QgsDebugMsg( "unable to find title element" );
547  return;
548  }
549 
550  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
551 
552  if ( !titleTextNode.isText() )
553  {
554  QgsDebugMsg( "unable to find title element" );
555  return;
556  }
557 
558  QDomText titleText = titleTextNode.toText();
559 
560  title = titleText.data();
561 
562 }
563 
565 {
566  QDomNodeList nl = doc.elementsByTagName( "qgis" );
567 
568  if ( !nl.count() )
569  {
570  QgsDebugMsg( " unable to find qgis element in project file" );
571  return QgsProjectVersion( 0, 0, 0, QString() );
572  }
573 
574  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
575 
576  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
577  QgsProjectVersion projectVersion( qgisElement.attribute( "version" ) );
578  return projectVersion;
579 }
580 
581 void QgsProject::processLayerJoins( QgsVectorLayer* layer )
582 {
583  if ( !layer )
584  return;
585 
586  layer->createJoinCaches();
587  layer->updateFields();
588 }
589 
590 bool QgsProject::_getMapLayers( const QDomDocument& doc, QList<QDomNode>& brokenNodes )
591 {
592  // Layer order is set by the restoring the legend settings from project file.
593  // This is done on the 'readProject( ... )' signal
594 
595  QDomNodeList nl = doc.elementsByTagName( "maplayer" );
596 
597  // process the map layer nodes
598 
599  if ( 0 == nl.count() ) // if we have no layers to process, bail
600  {
601  return true; // Decided to return "true" since it's
602  // possible for there to be a project with no
603  // layers; but also, more imporantly, this
604  // would cause the tests/qgsproject to fail
605  // since the test suite doesn't currently
606  // support test layers
607  }
608 
609  bool returnStatus = true;
610 
611  emit layerLoaded( 0, nl.count() );
612 
613  // order layers based on their dependencies
614  QgsLayerDefinition::DependencySorter depSorter( doc );
615  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
616  return false;
617 
618  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
619 
620  // Collect vector layers with joins.
621  // They need to refresh join caches and symbology infos after all layers are loaded
623  int i = 0;
624  Q_FOREACH ( const QDomNode& node, sortedLayerNodes )
625  {
626  QDomElement element = node.toElement();
627 
628  QString name = node.namedItem( "layername" ).toElement().text();
629  if ( !name.isNull() )
630  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
631 
632  if ( element.attribute( "embedded" ) == "1" )
633  {
634  createEmbeddedLayer( element.attribute( "id" ), readPath( element.attribute( "project" ) ), brokenNodes, vLayerList );
635  continue;
636  }
637  else
638  {
639  if ( !addLayer( element, brokenNodes, vLayerList ) )
640  {
641  returnStatus = false;
642  }
643  }
644  emit layerLoaded( i + 1, nl.count() );
645  i++;
646  }
647 
648  // Update field map of layers with joins and create join caches if necessary
649  // Needs to be done here once all dependent layers are loaded
650  QList< QPair< QgsVectorLayer*, QDomElement > >::iterator vIt = vLayerList.begin();
651  for ( ; vIt != vLayerList.end(); ++vIt )
652  {
653  processLayerJoins( vIt->first );
654  }
655 
656  QSet<QgsVectorLayer *> notified;
657  for ( vIt = vLayerList.begin(); vIt != vLayerList.end(); ++vIt )
658  {
659  if ( notified.contains( vIt->first ) )
660  continue;
661 
662  notified << vIt->first;
663  emit readMapLayer( vIt->first, vIt->second );
664  }
665 
666  return returnStatus;
667 }
668 
669 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList )
670 {
671  QString type = layerElem.attribute( "type" );
672  QgsDebugMsg( "Layer type is " + type );
673  QgsMapLayer *mapLayer = nullptr;
674 
675  if ( type == "vector" )
676  {
677  mapLayer = new QgsVectorLayer;
678  }
679  else if ( type == "raster" )
680  {
681  mapLayer = new QgsRasterLayer;
682  }
683  else if ( type == "plugin" )
684  {
685  QString typeName = layerElem.attribute( "name" );
686  mapLayer = QgsPluginLayerRegistry::instance()->createLayer( typeName );
687  }
688 
689  if ( !mapLayer )
690  {
691  QgsDebugMsg( "Unable to create layer" );
692 
693  return false;
694  }
695 
696  Q_CHECK_PTR( mapLayer );
697 
698  // have the layer restore state that is stored in Dom node
699  if ( mapLayer->readLayerXML( layerElem ) && mapLayer->isValid() )
700  {
701  // postpone readMapLayer signal for vector layers with joins
702  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
703  if ( !vLayer || vLayer->vectorJoins().isEmpty() )
704  emit readMapLayer( mapLayer, layerElem );
705  else
706  vectorLayerList.push_back( qMakePair( vLayer, layerElem ) );
707 
708  QList<QgsMapLayer *> myLayers;
709  myLayers << mapLayer;
711 
712  return true;
713  }
714  else
715  {
716  delete mapLayer;
717 
718  QgsDebugMsg( "Unable to load " + type + " layer" );
719  brokenNodes.push_back( layerElem );
720  return false;
721  }
722 }
723 
725 {
726  imp_->file.setFileName( file.filePath() );
727 
728  return read();
729 }
730 
732 {
733  clearError();
734 
735  QScopedPointer<QDomDocument> doc( new QDomDocument( "qgis" ) );
736 
737  if ( !imp_->file.open( QIODevice::ReadOnly | QIODevice::Text ) )
738  {
739  imp_->file.close();
740 
741  setError( tr( "Unable to open %1" ).arg( imp_->file.fileName() ) );
742 
743  return false;
744  }
745 
746  // location of problem associated with errorMsg
747  int line, column;
748  QString errorMsg;
749 
750  if ( !doc->setContent( &imp_->file, &errorMsg, &line, &column ) )
751  {
752  // want to make this class as GUI independent as possible; so commented out
753 #if 0
754  QMessageBox::critical( 0, tr( "Project File Read Error" ),
755  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
756 #endif
757 
758  QString errorString = tr( "Project file read error: %1 at line %2 column %3" )
759  .arg( errorMsg ).arg( line ).arg( column );
760 
761  QgsDebugMsg( errorString );
762 
763  imp_->file.close();
764 
765  setError( tr( "%1 for file %2" ).arg( errorString, imp_->file.fileName() ) );
766 
767  return false;
768  }
769 
770  imp_->file.close();
771 
772 
773  QgsDebugMsg( "Opened document " + imp_->file.fileName() );
774  QgsDebugMsg( "Project title: " + imp_->title );
775 
776  // get project version string, if any
777  QgsProjectVersion fileVersion = getVersion( *doc );
778  QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
779 
780  if ( thisVersion > fileVersion )
781  {
782  QgsLogger::warning( "Loading a file that was saved with an older "
783  "version of qgis (saved in " + fileVersion.text() +
784  ", loaded in " + QGis::QGIS_VERSION +
785  "). Problems may occur." );
786 
787  QgsProjectFileTransform projectFile( *doc, fileVersion );
788 
790  emit oldProjectVersionWarning( fileVersion.text() );
791  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
792 
793  projectFile.updateRevision( thisVersion );
794  }
795 
796  // start new project, just keep the file name
797  QString fileName = imp_->file.fileName();
798  clear();
799  imp_->file.setFileName( fileName );
800 
801  // now get any properties
802  _getProperties( *doc, imp_->properties_ );
803 
804  QgsDebugMsg( QString::number( imp_->properties_.count() ) + " properties read" );
805 
806  dump_( imp_->properties_ );
807 
808  // now get project title
809  _getTitle( *doc, imp_->title );
810 
811  QDomNodeList nl = doc->elementsByTagName( "autotransaction" );
812  if ( nl.count() )
813  {
814  QDomElement transactionElement = nl.at( 0 ).toElement();
815  if ( transactionElement.attribute( "active", "0" ).toInt() == 1 )
816  imp_->autoTransaction = true;
817  }
818 
819  nl = doc->elementsByTagName( "evaluateDefaultValues" );
820  if ( nl.count() )
821  {
822  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
823  if ( evaluateDefaultValuesElement.attribute( "active", "0" ).toInt() == 1 )
824  imp_->evaluateDefaultValues = true;
825  }
826 
827  // read the layer tree from project file
828 
829  mRootGroup->setCustomProperty( "loading", 1 );
830 
831  QDomElement layerTreeElem = doc->documentElement().firstChildElement( "layer-tree-group" );
832  if ( !layerTreeElem.isNull() )
833  {
834  mRootGroup->readChildrenFromXML( layerTreeElem );
835  }
836  else
837  {
838  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( "legend" ) );
839  }
840 
841  QgsDebugMsg( "Loaded layer tree:\n " + mRootGroup->dump() );
842 
843  mLayerTreeRegistryBridge->setEnabled( false );
844 
845  // get the map layers
846  QList<QDomNode> brokenNodes;
847  bool clean = _getMapLayers( *doc, brokenNodes );
848 
849  // review the integrity of the retrieved map layers
850  if ( !clean )
851  {
852  QgsDebugMsg( "Unable to get map layers from project file." );
853 
854  if ( !brokenNodes.isEmpty() )
855  {
856  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
857  }
858 
859  // we let a custom handler to decide what to do with missing layers
860  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
861  mBadLayerHandler->handleBadLayers( brokenNodes, *doc );
862  }
863 
864  mLayerTreeRegistryBridge->setEnabled( true );
865 
866  // load embedded groups and layers
867  loadEmbeddedNodes( mRootGroup );
868 
869  // make sure the are just valid layers
871 
872  mRootGroup->removeCustomProperty( "loading" );
873 
874  mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
875  mVisibilityPresetCollection->readXML( *doc );
876 
877 
878  // read the project: used by map canvas and legend
879  emit readProject( *doc );
880 
881  // if all went well, we're allegedly in pristine state
882  if ( clean )
883  setDirty( false );
884 
886 
887  return true;
888 }
889 
890 
892 {
893  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
894  {
895  if ( QgsLayerTree::isGroup( child ) )
896  {
897  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
898  if ( childGroup->customProperty( "embedded" ).toInt() )
899  {
900  // make sure to convert the path from relative to absolute
901  QString projectPath = readPath( childGroup->customProperty( "embedded_project" ).toString() );
902  childGroup->setCustomProperty( "embedded_project", projectPath );
903 
904  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( "embedded-invisible-layers" ).toStringList() );
905  if ( newGroup )
906  {
907  QList<QgsLayerTreeNode*> clonedChildren;
908  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
909  clonedChildren << newGroupChild->clone();
910  delete newGroup;
911 
912  childGroup->insertChildNodes( 0, clonedChildren );
913  }
914  }
915  else
916  {
917  loadEmbeddedNodes( childGroup );
918  }
919  }
920  else if ( QgsLayerTree::isLayer( child ) )
921  {
922  if ( child->customProperty( "embedded" ).toInt() )
923  {
924  QList<QDomNode> brokenNodes;
926  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( "embedded_project" ).toString(), brokenNodes, vectorLayerList );
927  }
928  }
929 
930  }
931 }
932 
933 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer*>& layers )
934 {
935  Q_FOREACH ( QgsMapLayer* layer, layers )
936  {
937  QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( layer );
938  if ( vlayer )
939  {
940  if ( autoTransaction() )
941  {
942  if ( QgsTransaction::supportsTransaction( vlayer ) )
943  {
944  QString connString = QgsDataSourceURI( vlayer->source() ).connectionInfo();
945  QString key = vlayer->providerType();
946 
947  QgsTransactionGroup* tg = mTransactionGroups.value( qMakePair( key, connString ) );
948 
949  if ( !tg )
950  {
951  tg = new QgsTransactionGroup();
952  mTransactionGroups.insert( qMakePair( key, connString ), tg );
953 
954  connect( tg, SIGNAL( commitError( QString ) ), this, SLOT( displayMapToolMessage( QString ) ) );
955  }
956  tg->addLayer( vlayer );
957  }
958  }
960  }
961 
962  connect( layer, SIGNAL( configChanged() ), this, SLOT( setDirty() ) );
963  }
964 }
965 
966 void QgsProject::cleanTransactionGroups( bool force )
967 {
968  for ( QMap< QPair< QString, QString>, QgsTransactionGroup*>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
969  {
970  if ( tg.value()->isEmpty() || force )
971  {
972  delete tg.value();
973  tg = mTransactionGroups.erase( tg );
974  }
975  else
976  {
977  ++tg;
978  }
979  }
980 }
981 
982 bool QgsProject::read( QDomNode &layerNode )
983 {
984  QList<QDomNode> brokenNodes;
986  if ( addLayer( layerNode.toElement(), brokenNodes, vectorLayerList ) )
987  {
988  // have to try to update joins for all layers now - a previously added layer may be dependant on this newly
989  // added layer for joins
991  Q_FOREACH ( QgsVectorLayer* layer, vectorLayers )
992  {
993  processLayerJoins( layer );
994  }
995 
996  if ( !vectorLayerList.isEmpty() )
997  {
998  emit readMapLayer( vectorLayerList.at( 0 ).first, vectorLayerList.at( 0 ).second );
999  }
1000 
1001  return true;
1002  }
1003  return false;
1004 }
1005 
1007 {
1008  imp_->file.setFileName( file.filePath() );
1009 
1010  return write();
1011 }
1012 
1014 {
1015  clearError();
1016 
1017  // if we have problems creating or otherwise writing to the project file,
1018  // let's find out up front before we go through all the hand-waving
1019  // necessary to create all the Dom objects
1020  QFileInfo myFileInfo( imp_->file );
1021  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1022  {
1023  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1024  .arg( imp_->file.fileName() ) );
1025  return false;
1026  }
1027 
1028  QDomImplementation DomImplementation;
1029  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1030 
1031  QDomDocumentType documentType =
1032  DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd",
1033  "SYSTEM" );
1034  QScopedPointer<QDomDocument> doc( new QDomDocument( documentType ) );
1035 
1036  QDomElement qgisNode = doc->createElement( "qgis" );
1037  qgisNode.setAttribute( "projectname", title() );
1038  qgisNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
1039 
1040  doc->appendChild( qgisNode );
1041 
1042  // title
1043  QDomElement titleNode = doc->createElement( "title" );
1044  qgisNode.appendChild( titleNode );
1045 
1046  QDomElement transactionNode = doc->createElement( "autotransaction" );
1047  transactionNode.setAttribute( "active", imp_->autoTransaction ? "1" : "0" );
1048  qgisNode.appendChild( transactionNode );
1049 
1050  QDomElement evaluateDefaultValuesNode = doc->createElement( "evaluateDefaultValues" );
1051  evaluateDefaultValuesNode.setAttribute( "active", imp_->evaluateDefaultValues ? "1" : "0" );
1052  qgisNode.appendChild( evaluateDefaultValuesNode );
1053 
1054  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1055  titleNode.appendChild( titleText );
1056 
1057  // write layer tree - make sure it is without embedded subgroups
1058  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1060  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ) ); // convert absolute paths to relative paths if required
1061  clonedRoot->writeXML( qgisNode );
1062  delete clonedRoot;
1063 
1064  // let map canvas and legend write their information
1065  emit writeProject( *doc );
1066 
1067  // within top level node save list of layers
1069 
1070  // Iterate over layers in zOrder
1071  // Call writeXML() on each
1072  QDomElement projectLayersNode = doc->createElement( "projectlayers" );
1073 
1075  while ( li != layers.end() )
1076  {
1077  QgsMapLayer *ml = li.value();
1078 
1079  if ( ml )
1080  {
1081  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1082  if ( emIt == mEmbeddedLayers.constEnd() )
1083  {
1084  // general layer metadata
1085  QDomElement maplayerElem = doc->createElement( "maplayer" );
1086 
1087  ml->writeLayerXML( maplayerElem, *doc );
1088 
1089  emit writeMapLayer( ml, maplayerElem, *doc );
1090 
1091  projectLayersNode.appendChild( maplayerElem );
1092  }
1093  else
1094  {
1095  // layer defined in an external project file
1096  // only save embedded layer if not managed by a legend group
1097  if ( emIt.value().second )
1098  {
1099  QDomElement mapLayerElem = doc->createElement( "maplayer" );
1100  mapLayerElem.setAttribute( "embedded", 1 );
1101  mapLayerElem.setAttribute( "project", writePath( emIt.value().first ) );
1102  mapLayerElem.setAttribute( "id", ml->id() );
1103  projectLayersNode.appendChild( mapLayerElem );
1104  }
1105  }
1106  }
1107  li++;
1108  }
1109 
1110  qgisNode.appendChild( projectLayersNode );
1111 
1112  // now add the optional extra properties
1113 
1114  dump_( imp_->properties_ );
1115 
1116  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( imp_->properties_.count() ) ) );
1117 
1118  if ( !imp_->properties_.isEmpty() ) // only worry about properties if we
1119  // actually have any properties
1120  {
1121  imp_->properties_.writeXML( "properties", qgisNode, *doc );
1122  }
1123 
1124  mVisibilityPresetCollection->writeXML( *doc );
1125 
1126  // now wrap it up and ship it to the project file
1127  doc->normalize(); // XXX I'm not entirely sure what this does
1128 
1129  // Create backup file
1130  if ( QFile::exists( fileName() ) )
1131  {
1132  QFile backupFile( fileName() + '~' );
1133  bool ok = true;
1134  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1135  ok &= imp_->file.open( QIODevice::ReadOnly );
1136 
1137  QByteArray ba;
1138  while ( ok && !imp_->file.atEnd() )
1139  {
1140  ba = imp_->file.read( 10240 );
1141  ok &= backupFile.write( ba ) == ba.size();
1142  }
1143 
1144  imp_->file.close();
1145  backupFile.close();
1146 
1147  if ( !ok )
1148  {
1149  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1150  return false;
1151  }
1152 
1153  QFileInfo fi( fileName() );
1154  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1155  utime( backupFile.fileName().toUtf8().constData(), &tb );
1156  }
1157 
1158  if ( !imp_->file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1159  {
1160  imp_->file.close(); // even though we got an error, let's make
1161  // sure it's closed anyway
1162 
1163  setError( tr( "Unable to save to file %1" ).arg( imp_->file.fileName() ) );
1164  return false;
1165  }
1166 
1167  QTemporaryFile tempFile;
1168  bool ok = tempFile.open();
1169  if ( ok )
1170  {
1171  QTextStream projectFileStream( &tempFile );
1172  doc->save( projectFileStream, 2 ); // save as utf-8
1173  ok &= projectFileStream.pos() > -1;
1174 
1175  ok &= tempFile.seek( 0 );
1176 
1177  QByteArray ba;
1178  while ( ok && !tempFile.atEnd() )
1179  {
1180  ba = tempFile.read( 10240 );
1181  ok &= imp_->file.write( ba ) == ba.size();
1182  }
1183 
1184  ok &= imp_->file.error() == QFile::NoError;
1185 
1186  imp_->file.close();
1187  }
1188 
1189  tempFile.close();
1190 
1191  if ( !ok )
1192  {
1193  setError( tr( "Unable to save to file %1. Your project "
1194  "may be corrupted on disk. Try clearing some space on the volume and "
1195  "check file permissions before pressing save again." )
1196  .arg( imp_->file.fileName() ) );
1197  return false;
1198  }
1199 
1200  setDirty( false ); // reset to pristine state
1201 
1202  emit projectSaved();
1203 
1204  return true;
1205 }
1206 
1208 {
1209  clear();
1210 
1211  setDirty( true );
1212 }
1213 
1214 bool QgsProject::writeEntry( const QString& scope, QString const& key, bool value )
1215 {
1216  setDirty( true );
1217 
1218  return addKey_( scope, key, &imp_->properties_, value );
1219 }
1220 
1221 bool QgsProject::writeEntry( const QString& scope, const QString& key,
1222  double value )
1223 {
1224  setDirty( true );
1225 
1226  return addKey_( scope, key, &imp_->properties_, value );
1227 }
1228 
1229 bool QgsProject::writeEntry( const QString& scope, QString const& key, int value )
1230 {
1231  setDirty( true );
1232 
1233  return addKey_( scope, key, &imp_->properties_, value );
1234 }
1235 
1236 bool QgsProject::writeEntry( const QString& scope, const QString &key,
1237  const QString& value )
1238 {
1239  setDirty( true );
1240 
1241  return addKey_( scope, key, &imp_->properties_, value );
1242 }
1243 
1244 bool QgsProject::writeEntry( const QString& scope, const QString& key,
1245  const QStringList& value )
1246 {
1247  setDirty( true );
1248 
1249  return addKey_( scope, key, &imp_->properties_, value );
1250 }
1251 
1253  const QString &key,
1254  const QStringList& def,
1255  bool *ok ) const
1256 {
1257  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1258 
1259  QVariant value;
1260 
1261  if ( property )
1262  {
1263  value = property->value();
1264 
1265  bool valid = QVariant::StringList == value.type();
1266  if ( ok )
1267  *ok = valid;
1268 
1269  if ( valid )
1270  {
1271  return value.toStringList();
1272  }
1273  }
1274 
1275  return def;
1276 }
1277 
1278 
1280  const QString& key,
1281  const QString& def,
1282  bool* ok ) const
1283 {
1284  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1285 
1286  QVariant value;
1287 
1288  if ( property )
1289  {
1290  value = property->value();
1291 
1292  bool valid = value.canConvert( QVariant::String );
1293  if ( ok )
1294  *ok = valid;
1295 
1296  if ( valid )
1297  return value.toString();
1298  }
1299 
1300  return def;
1301 }
1302 
1303 int QgsProject::readNumEntry( const QString& scope, const QString &key, int def,
1304  bool* ok ) const
1305 {
1306  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1307 
1308  QVariant value;
1309 
1310  if ( property )
1311  {
1312  value = property->value();
1313  }
1314 
1315  bool valid = value.canConvert( QVariant::String );
1316 
1317  if ( ok )
1318  {
1319  *ok = valid;
1320  }
1321 
1322  if ( valid )
1323  {
1324  return value.toInt();
1325  }
1326 
1327  return def;
1328 }
1329 
1330 double QgsProject::readDoubleEntry( const QString& scope, const QString& key,
1331  double def,
1332  bool* ok ) const
1333 {
1334  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1335  if ( property )
1336  {
1337  QVariant value = property->value();
1338 
1339  bool valid = value.canConvert( QVariant::Double );
1340  if ( ok )
1341  *ok = valid;
1342 
1343  if ( valid )
1344  return value.toDouble();
1345  }
1346 
1347  return def;
1348 }
1349 
1350 bool QgsProject::readBoolEntry( const QString& scope, const QString &key, bool def,
1351  bool* ok ) const
1352 {
1353  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1354 
1355  if ( property )
1356  {
1357  QVariant value = property->value();
1358 
1359  bool valid = value.canConvert( QVariant::Bool );
1360  if ( ok )
1361  *ok = valid;
1362 
1363  if ( valid )
1364  return value.toBool();
1365  }
1366 
1367  return def;
1368 }
1369 
1370 
1371 bool QgsProject::removeEntry( const QString& scope, const QString& key )
1372 {
1373  removeKey_( scope, key, imp_->properties_ );
1374 
1375  setDirty( true );
1376 
1377  return !findKey_( scope, key, imp_->properties_ );
1378 }
1379 
1380 
1381 QStringList QgsProject::entryList( const QString& scope, const QString& key ) const
1382 {
1383  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1384 
1385  QStringList entries;
1386 
1387  if ( foundProperty )
1388  {
1389  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1390 
1391  if ( propertyKey )
1392  { propertyKey->entryList( entries ); }
1393  }
1394 
1395  return entries;
1396 }
1397 
1398 QStringList QgsProject::subkeyList( const QString& scope, const QString& key ) const
1399 {
1400  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1401 
1402  QStringList entries;
1403 
1404  if ( foundProperty )
1405  {
1406  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1407 
1408  if ( propertyKey )
1409  { propertyKey->subkeyList( entries ); }
1410  }
1411 
1412  return entries;
1413 }
1414 
1416 {
1417  dump_( imp_->properties_ );
1418 }
1419 
1421 {
1422  if ( readBoolEntry( "Paths", "/Absolute", false ) )
1423  {
1424  return src;
1425  }
1426 
1427  // if this is a VSIFILE, remove the VSI prefix and append to final result
1428  QString vsiPrefix = qgsVsiPrefix( src );
1429  if ( ! vsiPrefix.isEmpty() )
1430  {
1431  // unfortunately qgsVsiPrefix returns prefix also for files like "/x/y/z.gz"
1432  // so we need to check if we really have the prefix
1433  if ( src.startsWith( "/vsi", Qt::CaseInsensitive ) )
1434  src.remove( 0, vsiPrefix.size() );
1435  else
1436  vsiPrefix.clear();
1437  }
1438 
1439  // relative path should always start with ./ or ../
1440  if ( !src.startsWith( "./" ) && !src.startsWith( "../" ) )
1441  {
1442 #if defined(Q_OS_WIN)
1443  if ( src.startsWith( "\\\\" ) ||
1444  src.startsWith( "//" ) ||
1445  ( src[0].isLetter() && src[1] == ':' ) )
1446  {
1447  // UNC or absolute path
1448  return vsiPrefix + src;
1449  }
1450 #else
1451  if ( src[0] == '/' )
1452  {
1453  // absolute path
1454  return vsiPrefix + src;
1455  }
1456 #endif
1457 
1458  // so this one isn't absolute, but also doesn't start // with ./ or ../.
1459  // That means that it was saved with an earlier version of "relative path support",
1460  // where the source file had to exist and only the project directory was stripped
1461  // from the filename.
1462  QString home = homePath();
1463  if ( home.isNull() )
1464  return vsiPrefix + src;
1465 
1466  QFileInfo fi( home + '/' + src );
1467 
1468  if ( !fi.exists() )
1469  {
1470  return vsiPrefix + src;
1471  }
1472  else
1473  {
1474  return vsiPrefix + fi.canonicalFilePath();
1475  }
1476  }
1477 
1478  QString srcPath = src;
1479  QString projPath = fileName();
1480 
1481  if ( projPath.isEmpty() )
1482  {
1483  return vsiPrefix + src;
1484  }
1485 
1486 #if defined(Q_OS_WIN)
1487  srcPath.replace( '\\', '/' );
1488  projPath.replace( '\\', '/' );
1489 
1490  bool uncPath = projPath.startsWith( "//" );
1491 #endif
1492 
1493  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1494  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1495 
1496 #if defined(Q_OS_WIN)
1497  if ( uncPath )
1498  {
1499  projElems.insert( 0, "" );
1500  projElems.insert( 0, "" );
1501  }
1502 #endif
1503 
1504  // remove project file element
1505  projElems.removeLast();
1506 
1507  // append source path elements
1508  projElems << srcElems;
1509  projElems.removeAll( "." );
1510 
1511  // resolve ..
1512  int pos;
1513  while (( pos = projElems.indexOf( ".." ) ) > 0 )
1514  {
1515  // remove preceding element and ..
1516  projElems.removeAt( pos - 1 );
1517  projElems.removeAt( pos - 1 );
1518  }
1519 
1520 #if !defined(Q_OS_WIN)
1521  // make path absolute
1522  projElems.prepend( "" );
1523 #endif
1524 
1525  return vsiPrefix + projElems.join( "/" );
1526 }
1527 
1528 QString QgsProject::writePath( const QString& src, const QString& relativeBasePath ) const
1529 {
1530  if ( readBoolEntry( "Paths", "/Absolute", false ) || src.isEmpty() )
1531  {
1532  return src;
1533  }
1534 
1535  QFileInfo srcFileInfo( src );
1536  QFileInfo projFileInfo( fileName() );
1537  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
1538  QString projPath = projFileInfo.canonicalFilePath();
1539 
1540  if ( !relativeBasePath.isNull() )
1541  {
1542  projPath = relativeBasePath;
1543  }
1544 
1545  if ( projPath.isEmpty() )
1546  {
1547  return src;
1548  }
1549 
1550  // if this is a VSIFILE, remove the VSI prefix and append to final result
1551  QString vsiPrefix = qgsVsiPrefix( src );
1552  if ( ! vsiPrefix.isEmpty() )
1553  {
1554  srcPath.remove( 0, vsiPrefix.size() );
1555  }
1556 
1557 #if defined( Q_OS_WIN )
1558  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1559 
1560  srcPath.replace( '\\', '/' );
1561 
1562  if ( srcPath.startsWith( "//" ) )
1563  {
1564  // keep UNC prefix
1565  srcPath = "\\\\" + srcPath.mid( 2 );
1566  }
1567 
1568  projPath.replace( '\\', '/' );
1569  if ( projPath.startsWith( "//" ) )
1570  {
1571  // keep UNC prefix
1572  projPath = "\\\\" + projPath.mid( 2 );
1573  }
1574 #else
1575  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1576 #endif
1577 
1578  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1579  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1580 
1581  // remove project file element
1582  projElems.removeLast();
1583 
1584  projElems.removeAll( "." );
1585  srcElems.removeAll( "." );
1586 
1587  // remove common part
1588  int n = 0;
1589  while ( !srcElems.isEmpty() &&
1590  !projElems.isEmpty() &&
1591  srcElems[0].compare( projElems[0], cs ) == 0 )
1592  {
1593  srcElems.removeFirst();
1594  projElems.removeFirst();
1595  n++;
1596  }
1597 
1598  if ( n == 0 )
1599  {
1600  // no common parts; might not even by a file
1601  return src;
1602  }
1603 
1604  if ( !projElems.isEmpty() )
1605  {
1606  // go up to the common directory
1607  for ( int i = 0; i < projElems.size(); i++ )
1608  {
1609  srcElems.insert( 0, ".." );
1610  }
1611  }
1612  else
1613  {
1614  // let it start with . nevertheless,
1615  // so relative path always start with either ./ or ../
1616  srcElems.insert( 0, "." );
1617  }
1618 
1619  return vsiPrefix + srcElems.join( "/" );
1620 }
1621 
1622 void QgsProject::setError( const QString& errorMessage )
1623 {
1624  mErrorMessage = errorMessage;
1625 }
1626 
1628 {
1629  return mErrorMessage;
1630 }
1631 
1633 {
1634  setError( QString() );
1635 }
1636 
1638 {
1639  delete mBadLayerHandler;
1640  mBadLayerHandler = handler;
1641 }
1642 
1644 {
1645  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1646  if ( it == mEmbeddedLayers.constEnd() )
1647  {
1648  return QString();
1649  }
1650  return it.value().first;
1651 }
1652 
1653 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1654  QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList, bool saveFlag )
1655 {
1656  QgsDebugCall;
1657 
1658  static QString prevProjectFilePath;
1659  static QDateTime prevProjectFileTimestamp;
1660  static QDomDocument projectDocument;
1661 
1662  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1663 
1664  if ( projectFilePath != prevProjectFilePath || projectFileTimestamp != prevProjectFileTimestamp )
1665  {
1666  prevProjectFilePath.clear();
1667 
1668  QFile projectFile( projectFilePath );
1669  if ( !projectFile.open( QIODevice::ReadOnly ) )
1670  {
1671  return false;
1672  }
1673 
1674  if ( !projectDocument.setContent( &projectFile ) )
1675  {
1676  return false;
1677  }
1678 
1679  prevProjectFilePath = projectFilePath;
1680  prevProjectFileTimestamp = projectFileTimestamp;
1681  }
1682 
1683  // does project store pathes absolute or relative?
1684  bool useAbsolutePathes = true;
1685 
1686  QDomElement propertiesElem = projectDocument.documentElement().firstChildElement( "properties" );
1687  if ( !propertiesElem.isNull() )
1688  {
1689  QDomElement absElem = propertiesElem.firstChildElement( "Paths" ).firstChildElement( "Absolute" );
1690  if ( !absElem.isNull() )
1691  {
1692  useAbsolutePathes = absElem.text().compare( "true", Qt::CaseInsensitive ) == 0;
1693  }
1694  }
1695 
1696  QDomElement projectLayersElem = projectDocument.documentElement().firstChildElement( "projectlayers" );
1697  if ( projectLayersElem.isNull() )
1698  {
1699  return false;
1700  }
1701 
1702  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( "maplayer" );
1703  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1704  {
1705  // get layer id
1706  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1707  QString id = mapLayerElem.firstChildElement( "id" ).text();
1708  if ( id == layerId )
1709  {
1710  // layer can be embedded only once
1711  if ( mapLayerElem.attribute( "embedded" ) == "1" )
1712  {
1713  return false;
1714  }
1715 
1716  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1717 
1718  // change datasource path from relative to absolute if necessary
1719  // see also QgsMapLayer::readLayerXML
1720  if ( !useAbsolutePathes )
1721  {
1722  QString provider( mapLayerElem.firstChildElement( "provider" ).text() );
1723  QDomElement dsElem( mapLayerElem.firstChildElement( "datasource" ) );
1724  QString datasource( dsElem.text() );
1725  if ( provider == "spatialite" )
1726  {
1727  QgsDataSourceURI uri( datasource );
1728  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + uri.database() );
1729  if ( absoluteDs.exists() )
1730  {
1731  uri.setDatabase( absoluteDs.absoluteFilePath() );
1732  datasource = uri.uri();
1733  }
1734  }
1735  else if ( provider == "ogr" )
1736  {
1737  QStringList theURIParts( datasource.split( '|' ) );
1738  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1739  if ( absoluteDs.exists() )
1740  {
1741  theURIParts[0] = absoluteDs.absoluteFilePath();
1742  datasource = theURIParts.join( "|" );
1743  }
1744  }
1745  else if ( provider == "gpx" )
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 == "delimitedtext" )
1756  {
1757  QUrl urlSource( QUrl::fromEncoded( datasource.toAscii() ) );
1758 
1759  if ( !datasource.startsWith( "file:" ) )
1760  {
1761  QUrl file( QUrl::fromLocalFile( datasource.left( datasource.indexOf( '?' ) ) ) );
1762  urlSource.setScheme( "file" );
1763  urlSource.setPath( file.path() );
1764  }
1765 
1766  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + urlSource.toLocalFile() );
1767  if ( absoluteDs.exists() )
1768  {
1769  QUrl urlDest = QUrl::fromLocalFile( absoluteDs.absoluteFilePath() );
1770  urlDest.setQueryItems( urlSource.queryItems() );
1771  datasource = QString::fromAscii( urlDest.toEncoded() );
1772  }
1773  }
1774  else
1775  {
1776  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + datasource );
1777  if ( absoluteDs.exists() )
1778  {
1779  datasource = absoluteDs.absoluteFilePath();
1780  }
1781  }
1782 
1783  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1784  dsElem.appendChild( projectDocument.createTextNode( datasource ) );
1785  }
1786 
1787  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1788  {
1789  return true;
1790  }
1791  else
1792  {
1793  mEmbeddedLayers.remove( layerId );
1794  return false;
1795  }
1796  }
1797  }
1798 
1799  return false;
1800 }
1801 
1802 
1803 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1804 {
1805  // open project file, get layer ids in group, add the layers
1806  QFile projectFile( projectFilePath );
1807  if ( !projectFile.open( QIODevice::ReadOnly ) )
1808  {
1809  return nullptr;
1810  }
1811 
1812  QDomDocument projectDocument;
1813  if ( !projectDocument.setContent( &projectFile ) )
1814  {
1815  return nullptr;
1816  }
1817 
1818  // store identify disabled layers of the embedded project
1819  QSet<QString> embeddedIdentifyDisabledLayers;
1820  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( "properties" ).firstChildElement( "Identify" ).firstChildElement( "disabledLayers" );
1821  if ( !disabledLayersElem.isNull() )
1822  {
1823  QDomNodeList valueList = disabledLayersElem.elementsByTagName( "value" );
1824  for ( int i = 0; i < valueList.size(); ++i )
1825  {
1826  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1827  }
1828  }
1829 
1831 
1832  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( "layer-tree-group" );
1833  if ( !layerTreeElem.isNull() )
1834  {
1835  root->readChildrenFromXML( layerTreeElem );
1836  }
1837  else
1838  {
1839  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( "legend" ) );
1840  }
1841 
1842  QgsLayerTreeGroup *group = root->findGroup( groupName );
1843  if ( !group || group->customProperty( "embedded" ).toBool() )
1844  {
1845  // embedded groups cannot be embedded again
1846  delete root;
1847  return nullptr;
1848  }
1849 
1850  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1851  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1852  delete root;
1853  root = nullptr;
1854 
1855  newGroup->setCustomProperty( "embedded", 1 );
1856  newGroup->setCustomProperty( "embedded_project", projectFilePath );
1857 
1858  // set "embedded" to all children + load embedded layers
1859  mLayerTreeRegistryBridge->setEnabled( false );
1860  initializeEmbeddedSubtree( projectFilePath, newGroup );
1861  mLayerTreeRegistryBridge->setEnabled( true );
1862 
1863  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
1864 
1865  // consider the layers might be identify disabled in its project
1866  Q_FOREACH ( const QString& layerId, newGroup->findLayerIds() )
1867  {
1868  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1869  {
1870  thisProjectIdentifyDisabledLayers.append( layerId );
1871  }
1872 
1873  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1874  if ( layer )
1875  {
1876  layer->setVisible( invisibleLayers.contains( layerId ) ? Qt::Unchecked : Qt::Checked );
1877  }
1878  }
1879 
1880  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
1881 
1882  return newGroup;
1883 }
1884 
1886 {
1887  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1888  {
1889  // all nodes in the subtree will have "embedded" custom property set
1890  child->setCustomProperty( "embedded", 1 );
1891 
1892  if ( QgsLayerTree::isGroup( child ) )
1893  {
1894  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1895  }
1896  else if ( QgsLayerTree::isLayer( child ) )
1897  {
1898  // load the layer into our project
1899  QList<QDomNode> brokenNodes;
1901  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, vectorLayerList, false );
1902  }
1903  }
1904 }
1905 
1906 void QgsProject::setSnapSettingsForLayer( const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
1907 {
1908  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1909  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1910  int idx = layerIdList.indexOf( layerId );
1911  if ( idx != -1 )
1912  {
1913  layerIdList.removeAt( idx );
1914  enabledList.removeAt( idx );
1915  snapTypeList.removeAt( idx );
1916  toleranceUnitList.removeAt( idx );
1917  toleranceList.removeAt( idx );
1918  avoidIntersectionList.removeOne( layerId );
1919  }
1920 
1921  layerIdList.append( layerId );
1922 
1923  // enabled
1924  enabledList.append( enabled ? "enabled" : "disabled" );
1925 
1926  // snap type
1927  QString typeString;
1928  if ( type == QgsSnapper::SnapToSegment )
1929  {
1930  typeString = "to_segment";
1931  }
1932  else if ( type == QgsSnapper::SnapToVertexAndSegment )
1933  {
1934  typeString = "to_vertex_and_segment";
1935  }
1936  else
1937  {
1938  typeString = "to_vertex";
1939  }
1940  snapTypeList.append( typeString );
1941 
1942  // units
1943  toleranceUnitList.append( QString::number( unit ) );
1944 
1945  // tolerance
1946  toleranceList.append( QString::number( tolerance ) );
1947 
1948  // avoid intersection
1949  if ( avoidIntersection )
1950  {
1951  avoidIntersectionList.append( layerId );
1952  }
1953 
1954  writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
1955  writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
1956  writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
1957  writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
1958  writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
1959  writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
1960  emit snapSettingsChanged();
1961 }
1962 
1963 bool QgsProject::snapSettingsForLayer( const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance,
1964  bool &avoidIntersection ) const
1965 {
1966  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1967  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1968  int idx = layerIdList.indexOf( layerId );
1969  if ( idx == -1 )
1970  {
1971  return false;
1972  }
1973 
1974  // make sure all lists are long enough
1975  int minListEntries = idx + 1;
1976  if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
1977  toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
1978  {
1979  return false;
1980  }
1981 
1982  // enabled
1983  enabled = enabledList.at( idx ) == "enabled";
1984 
1985  // snap type
1986  QString snapType = snapTypeList.at( idx );
1987  if ( snapType == "to_segment" )
1988  {
1990  }
1991  else if ( snapType == "to_vertex_and_segment" )
1992  {
1994  }
1995  else // to vertex
1996  {
1997  type = QgsSnapper::SnapToVertex;
1998  }
1999 
2000  // units
2001  if ( toleranceUnitList.at( idx ) == "1" )
2002  {
2003  units = QgsTolerance::Pixels;
2004  }
2005  else if ( toleranceUnitList.at( idx ) == "2" )
2006  {
2008  }
2009  else
2010  {
2011  units = QgsTolerance::LayerUnits;
2012  }
2013 
2014  // tolerance
2015  tolerance = toleranceList.at( idx ).toDouble();
2016 
2017  // avoid intersection
2018  avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
2019 
2020  return true;
2021 }
2022 
2023 void QgsProject::snapSettings( QStringList &layerIdList, QStringList &enabledList, QStringList &snapTypeList, QStringList &toleranceUnitList, QStringList &toleranceList,
2024  QStringList &avoidIntersectionList ) const
2025 {
2026  layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
2027  enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
2028  toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
2029  toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
2030  snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
2031  avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
2032 }
2033 
2035 {
2036  return imp_->evaluateDefaultValues;
2037 }
2038 
2040 {
2041  Q_FOREACH ( QgsMapLayer* layer, QgsMapLayerRegistry::instance()->mapLayers().values() )
2042  {
2043  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( layer );
2044  if ( vl )
2045  {
2047  }
2048  }
2049 
2050  imp_->evaluateDefaultValues = evaluateDefaultValues;
2051 }
2052 
2054 {
2055  QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
2056  emit snapSettingsChanged();
2057 }
2058 
2060 {
2061  return ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) > 0 );
2062 }
2063 
2065 {
2066  QString distanceUnitString = QgsProject::instance()->readEntry( "Measurement", "/DistanceUnits", QString() );
2067  if ( !distanceUnitString.isEmpty() )
2068  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2069 
2070  //fallback to QGIS default measurement unit
2071  QSettings s;
2072  bool ok = false;
2073  QGis::UnitType type = QgsUnitTypes::decodeDistanceUnit( s.value( "/qgis/measure/displayunits" ).toString(), &ok );
2074  return ok ? type : QGis::Meters;
2075 }
2076 
2078 {
2079  QString areaUnitString = QgsProject::instance()->readEntry( "Measurement", "/AreaUnits", QString() );
2080  if ( !areaUnitString.isEmpty() )
2081  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2082 
2083  //fallback to QGIS default area unit
2084  QSettings s;
2085  bool ok = false;
2086  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( "/qgis/measure/areaunits" ).toString(), &ok );
2087  return ok ? type : QgsUnitTypes::SquareMeters;
2088 }
2089 
2091 {
2092  // just ignore any bad layers
2093 }
2094 
2096 {
2097  QFileInfo pfi( fileName() );
2098  if ( !pfi.exists() )
2099  return QString::null;
2100 
2101  return pfi.canonicalPath();
2102 }
2103 
2105 {
2106  return mRelationManager;
2107 }
2108 
2110 {
2111  return mRootGroup;
2112 }
2113 
2115 {
2116  return mVisibilityPresetCollection.data();
2117 }
2118 
2120 {
2121  QStringList currentLayers = nonIdentifiableLayers();
2122 
2123  QStringList newLayers;
2124  Q_FOREACH ( QgsMapLayer* l, layers )
2125  {
2126  newLayers << l->id();
2127  }
2128 
2129  if ( newLayers == currentLayers )
2130  return;
2131 
2132  QStringList disabledLayerIds;
2133 
2134  Q_FOREACH ( QgsMapLayer* l, layers )
2135  {
2136  disabledLayerIds << l->id();
2137  }
2138 
2139  setNonIdentifiableLayers( disabledLayerIds );
2140 }
2141 
2143 {
2144  writeEntry( "Identify", "/disabledLayers", layerIds );
2145 
2146  emit nonIdentifiableLayersChanged( layerIds );
2147 }
2148 
2150 {
2151  return QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" );
2152 }
2153 
2155 {
2156  return imp_->autoTransaction;
2157 }
2158 
2160 {
2161  if ( autoTransaction != imp_->autoTransaction )
2162  {
2163  imp_->autoTransaction = autoTransaction;
2164 
2165  if ( autoTransaction )
2166  onMapLayersAdded( QgsMapLayerRegistry::instance()->mapLayers().values() );
2167  else
2168  cleanTransactionGroups( true );
2169  }
2170 }
2171 
2173 {
2174  return mTransactionGroups;
2175 }
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
static const char * QGIS_VERSION
Definition: qgis.h:46
bool canConvert(Type t) const
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
Get map layers of a certain 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:891
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:564
QStringList nonIdentifiableLayers() const
Get the list of layers which currently should not be taken into account on map identification.
QMap< QString, QgsMapLayer * > mapLayers()
Retrieve the mapLayers collection (mainly intended for use by projection)
int size() const
bool isNull() const
void setNonIdentifiableLayers(QList< QgsMapLayer * > layers)
Set a list of layers which should not be taken into account on map identification.
QString name() const
every key has a name
void clear()
QString filePath() const
QDomElement toElement() const
void setPath(const QString &path)
SnappingType
Snap to vertex, to segment or both.
Definition: qgssnapper.h:66
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
const char * name() const
bool writeEntry(const QString &scope, const QString &key, bool value)
QString canonicalFilePath() const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=nullptr) const
bool isText() const
int count() const
QString number(int n, int base)
int count(const T &value) const
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList, bool saveFlag=true)
Creates a maplayer instance defined in an arbitrary project file.
bool read()
Reads the current project file.
Definition: qgsproject.cpp:731
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:530
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.
Definition: qgsproject.cpp:422
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)
bool hasChildNodes() const
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:494
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:669
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:457
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 ...
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:427
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:535
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:432
Interface for classes that handle missing layer files when reading project file.
Definition: qgsproject.h:526
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