QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsproject.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsproject.cpp - description
3  -------------------
4  begin : July 23, 2004
5  copyright : (C) 2004 by Mark Coletti
6  email : mcoletti at gmail.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsproject.h"
19 
20 #include <deque>
21 #include <memory>
22 
23 #include "qgslogger.h"
24 #include "qgsrectangle.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsrasterlayer.h"
27 #include "qgsmaplayerregistry.h"
28 #include "qgsexception.h"
29 #include "qgsprojectproperty.h"
31 #include "qgsprojectversion.h"
32 #include "qgspluginlayer.h"
33 #include "qgspluginlayerregistry.h"
34 #include "qgsdatasourceuri.h"
35 
36 #include <QApplication>
37 #include <QFileInfo>
38 #include <QDomNode>
39 #include <QObject>
40 #include <QTextStream>
41 
42 // canonical project instance
44 
53 static
54 QStringList makeKeyTokens_( QString const &scope, QString const &key )
55 {
56  // XXX - debugger probes
57  //const char * scope_str = scope.toLocal8Bit().data();
58  //const char * key_str = key.toLocal8Bit().data();
59 
60  QStringList keyTokens = QStringList( scope );
61  keyTokens += key.split( '/', QString::SkipEmptyParts );
62 
63  // be sure to include the canonical root node
64  keyTokens.push_front( "properties" );
65 
66  return keyTokens;
67 } // makeKeyTokens_
68 
69 
70 
71 
81 static
82 QgsProperty * findKey_( QString const & scope,
83  QString const & key,
84  QgsPropertyKey & rootProperty )
85 {
86  QgsPropertyKey * currentProperty = &rootProperty;
87  QgsProperty * nextProperty; // link to next property down hiearchy
88 
89  QStringList keySequence = makeKeyTokens_( scope, key );
90 
91  while ( ! keySequence.isEmpty() )
92  {
93  // if the current head of the sequence list matches the property name,
94  // then traverse down the property hierarchy
95  if ( keySequence.first() == currentProperty->name() )
96  {
97  // remove front key since we're traversing down a level
98  keySequence.pop_front();
99 
100  // if we have only one key name left, then return the key found
101  if ( 1 == keySequence.count() )
102  {
103  return currentProperty->find( keySequence.front() );
104 
105  }
106  // if we're out of keys then the current property is the one we
107  // want; i.e., we're in the rate case of being at the top-most
108  // property node
109  else if ( keySequence.isEmpty() )
110  {
111  return currentProperty;
112  }
113  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
114  {
115  if ( nextProperty->isKey() )
116  {
117  currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
118  }
119  // it may be that this may be one of several property value
120  // nodes keyed by QDict string; if this is the last remaining
121  // key token and the next property is a value node, then
122  // that's the situation, so return the currentProperty
123  else if ( nextProperty->isValue() && ( 1 == keySequence.count() ) )
124  {
125  return currentProperty;
126  }
127  else // QgsPropertyValue not Key, so return null
128  {
129  return 0x0;
130  }
131  }
132  else // if the next key down isn't found
133  { // then the overall key sequence doesn't exist
134  return 0x0;
135  }
136  }
137  else
138  {
139  return 0x0;
140  }
141  }
142 
143  return 0x0;
144 } // findKey_
145 
146 
147 
155 static
156 QgsProperty * addKey_( QString const & scope,
157  QString const & key,
158  QgsPropertyKey * rootProperty,
159  QVariant value )
160 {
161  QStringList keySequence = makeKeyTokens_( scope, key );
162 
163  // cursor through property key/value hierarchy
164  QgsPropertyKey * currentProperty = rootProperty;
165 
166  QgsProperty * newProperty; // link to next property down hiearchy
167 
168  while ( ! keySequence.isEmpty() )
169  {
170  // if the current head of the sequence list matches the property name,
171  // then traverse down the property hierarchy
172  if ( keySequence.first() == currentProperty->name() )
173  {
174  // remove front key since we're traversing down a level
175  keySequence.pop_front();
176 
177  // if key sequence has one last element, then we use that as the
178  // name to store the value
179  if ( 1 == keySequence.count() )
180  {
181  currentProperty->setValue( keySequence.front(), value );
182  return currentProperty;
183  }
184  // we're at the top element if popping the keySequence element
185  // will leave it empty; in that case, just add the key
186  else if ( keySequence.isEmpty() )
187  {
188  currentProperty->setValue( value );
189 
190  return currentProperty;
191  }
192  else if (( newProperty = currentProperty->find( keySequence.first() ) ) )
193  {
194  currentProperty = dynamic_cast<QgsPropertyKey*>( newProperty );
195 
196  if ( currentProperty )
197  {
198  continue;
199  }
200  else // QgsPropertyValue not Key, so return null
201  {
202  return 0x0;
203  }
204  }
205  else // the next subkey doesn't exist, so add it
206  {
207  newProperty = currentProperty->addKey( keySequence.first() );
208 
209  if ( newProperty )
210  {
211  currentProperty = dynamic_cast<QgsPropertyKey*>( newProperty );
212  }
213  continue;
214  }
215  }
216  else
217  {
218  return 0x0;
219  }
220  }
221 
222  return 0x0;
223 
224 } // addKey_
225 
226 
227 
228 static
229 void removeKey_( QString const & scope,
230  QString const & key,
231  QgsPropertyKey & rootProperty )
232 {
233  QgsPropertyKey * currentProperty = &rootProperty;
234 
235  QgsProperty * nextProperty = NULL; // link to next property down hiearchy
236  QgsPropertyKey * previousQgsPropertyKey = NULL; // link to previous property up hiearchy
237 
238  QStringList keySequence = makeKeyTokens_( scope, key );
239 
240  while ( ! keySequence.isEmpty() )
241  {
242  // if the current head of the sequence list matches the property name,
243  // then traverse down the property hierarchy
244  if ( keySequence.first() == currentProperty->name() )
245  {
246  // remove front key since we're traversing down a level
247  keySequence.pop_front();
248 
249  // if we have only one key name left, then try to remove the key
250  // with that name
251  if ( 1 == keySequence.count() )
252  {
253  currentProperty->removeKey( keySequence.front() );
254  }
255  // if we're out of keys then the current property is the one we
256  // want to remove, but we can't delete it directly; we need to
257  // delete it from the parent property key container
258  else if ( keySequence.isEmpty() )
259  {
260  previousQgsPropertyKey->removeKey( currentProperty->name() );
261  }
262  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
263  {
264  previousQgsPropertyKey = currentProperty;
265  currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
266 
267  if ( currentProperty )
268  {
269  continue;
270  }
271  else // QgsPropertyValue not Key, so return null
272  {
273  return;
274  }
275  }
276  else // if the next key down isn't found
277  { // then the overall key sequence doesn't exist
278  return;
279  }
280  }
281  else
282  {
283  return;
284  }
285  }
286 
287 } // void removeKey_
288 
289 
290 
292 {
294  QFile file;
295 
298 
300  QString title;
301 
303  bool dirty;
304 
305  Imp()
306  : title( "" ),
307  dirty( false )
308  { // top property node is the root
309  // "properties" that contains all plug-in
310  // and extra property keys and values
311  properties_.name() = "properties"; // root property node always this value
312  }
313 
316  void clear()
317  {
318  //QgsDebugMsg( "Clearing project properties Impl->clear();" );
319 
321  title = "";
322 
323  // reset some default project properties
324  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
325  QgsProject::instance()->writeEntry( "PositionPrecision", "/Automatic", true );
326  QgsProject::instance()->writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
327  QgsProject::instance()->writeEntry( "Paths", "/Absolute", false );
328  }
329 
330 }; // struct QgsProject::Imp
331 
332 
333 
335  : imp_( new QgsProject::Imp ), mBadLayerHandler( new QgsProjectBadLayerDefaultHandler() )
336 {
337  // Set some default project properties
338  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
339  writeEntry( "PositionPrecision", "/Automatic", true );
340  writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
341  writeEntry( "Paths", "/Absolute", false );
342  // XXX writeEntry() makes the project dirty, but it doesn't make sense
343  // for a new project to be dirty, so let's clean it up
344  dirty( false );
345 } // QgsProject ctor
346 
347 
348 
350 {
351  delete mBadLayerHandler;
352 
353  // note that std::auto_ptr automatically deletes imp_ when it's destroyed
354 } // QgsProject dtor
355 
356 
357 
359 {
360  if ( !QgsProject::theProject_ )
361  {
362  QgsProject::theProject_ = new QgsProject;
363  }
364 
366 } // QgsProject * instance()
367 
368 
369 
370 
371 void QgsProject::title( QString const &title )
372 {
373  imp_->title = title;
374 
375  dirty( true );
376 } // void QgsProject::title
377 
378 
379 QString const & QgsProject::title() const
380 {
381  return imp_->title;
382 } // QgsProject::title() const
383 
384 
386 {
387  return imp_->dirty;
388 } // bool QgsProject::isDirty()
389 
390 
391 void QgsProject::dirty( bool b )
392 {
393  imp_->dirty = b;
394 } // bool QgsProject::isDirty()
395 
396 
397 
398 void QgsProject::setFileName( QString const &name )
399 {
400  imp_->file.setFileName( name );
401 
402  dirty( true );
403 } // void QgsProject::setFileName( QString const & name )
404 
405 
406 
407 QString QgsProject::fileName() const
408 {
409  return imp_->file.fileName();
410 } // QString QgsProject::fileName() const
411 
412 
413 
415 static void dump_( QgsPropertyKey const & topQgsPropertyKey )
416 {
417  QgsDebugMsg( "current properties:" );
418 
419  topQgsPropertyKey.dump();
420 } // dump_
421 
422 
423 
424 
455 static
456 void
457 _getProperties( QDomDocument const &doc, QgsPropertyKey & project_properties )
458 {
459  QDomNodeList properties = doc.elementsByTagName( "properties" );
460 
461  if ( properties.count() > 1 )
462  {
463  QgsDebugMsg( "there appears to be more than one ``properties'' XML tag ... bailing" );
464  return;
465  }
466  else if ( properties.count() < 1 ) // no properties found, so we're done
467  {
468  return;
469  }
470 
471  // item(0) because there should only be ONE
472  // "properties" node
473  QDomNodeList scopes = properties.item( 0 ).childNodes();
474 
475  if ( scopes.count() < 1 )
476  {
477  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
478  return;
479  }
480 
481  QDomNode propertyNode = properties.item( 0 );
482 
483  if ( ! project_properties.readXML( propertyNode ) )
484  {
485  QgsDebugMsg( "Project_properties.readXML() failed" );
486  }
487 
488 #if 0
489 // DEPRECATED as functionality has been shoved down to QgsProperyKey::readXML()
490  size_t i = 0;
491  while ( i < scopes.count() )
492  {
493  QDomNode curr_scope_node = scopes.item( i );
494 
495  qDebug( "found %d property node(s) for scope %s",
496  curr_scope_node.childNodes().count(),
497  curr_scope_node.nodeName().utf8().constData() );
498 
499  QString key( curr_scope_node.nodeName() );
500 
501  QgsPropertyKey * currentKey =
502  dynamic_cast<QgsPropertyKey*>( project_properties.find( key ) );
503 
504  if ( ! currentKey )
505  {
506  // if the property key doesn't yet exist, create an empty instance
507  // of that key
508 
509  currentKey = project_properties.addKey( key );
510 
511  if ( ! currentKey )
512  {
513  qDebug( "%s:%d unable to add key", __FILE__, __LINE__ );
514  }
515  }
516 
517  if ( ! currentKey->readXML( curr_scope_node ) )
518  {
519  qDebug( "%s:%d unable to read XML for property %s", __FILE__, __LINE__,
520  curr_scope_node.nodeName().utf8().constData() );
521  }
522 
523  ++i;
524  }
525 #endif
526 } // _getProperties
527 
528 
529 
530 
542 static void _getTitle( QDomDocument const &doc, QString & title )
543 {
544  QDomNodeList nl = doc.elementsByTagName( "title" );
545 
546  title = ""; // by default the title will be empty
547 
548  if ( !nl.count() )
549  {
550  QgsDebugMsg( "unable to find title element" );
551  return;
552  }
553 
554  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element ok
555 
556  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
557  {
558  QgsDebugMsg( "unable to find title element" );
559  return;
560  }
561 
562  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
563 
564  if ( !titleTextNode.isText() )
565  {
566  QgsDebugMsg( "unable to find title element" );
567  return;
568  }
569 
570  QDomText titleText = titleTextNode.toText();
571 
572  title = titleText.data();
573 
574 } // _getTitle
575 
576 
581 static QgsProjectVersion _getVersion( QDomDocument const &doc )
582 {
583  QDomNodeList nl = doc.elementsByTagName( "qgis" );
584 
585  if ( !nl.count() )
586  {
587  QgsDebugMsg( " unable to find qgis element in project file" );
588  return QgsProjectVersion( 0, 0, 0, QString( "" ) );
589  }
590 
591  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
592 
593  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
594  QgsProjectVersion projectVersion( qgisElement.attribute( "version" ) );
595  return projectVersion;
596 } // _getVersion
597 
598 
599 
647 QPair< bool, QList<QDomNode> > QgsProject::_getMapLayers( QDomDocument const &doc )
648 {
649  // Layer order is set by the restoring the legend settings from project file.
650  // This is done on the 'readProject( ... )' signal
651 
652  QDomNodeList nl = doc.elementsByTagName( "maplayer" );
653 
654  // XXX what is this used for? QString layerCount( QString::number(nl.count()) );
655 
656  QString wk;
657 
658  QList<QDomNode> brokenNodes; // a list of Dom nodes corresponding to layers
659  // that we were unable to load; this could be
660  // because the layers were removed or
661  // re-located after the project was last saved
662 
663  // process the map layer nodes
664 
665  if ( 0 == nl.count() ) // if we have no layers to process, bail
666  {
667  return qMakePair( true, brokenNodes ); // Decided to return "true" since it's
668  // possible for there to be a project with no
669  // layers; but also, more imporantly, this
670  // would cause the tests/qgsproject to fail
671  // since the test suite doesn't currently
672  // support test layers
673  }
674 
675  bool returnStatus = true;
676 
677  emit layerLoaded( 0, nl.count() );
678 
679  //Collect vector layers with joins.
680  //They need to refresh join caches and symbology infos after all layers are loaded
681  QList< QPair< QgsVectorLayer*, QDomElement > > vLayerList;
682 
683  for ( int i = 0; i < nl.count(); i++ )
684  {
685  QDomNode node = nl.item( i );
686  QDomElement element = node.toElement();
687 
688  QString name = node.namedItem( "layername" ).toElement().text();
689  if ( !name.isNull() )
690  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
691 
692  if ( element.attribute( "embedded" ) == "1" )
693  {
694  createEmbeddedLayer( element.attribute( "id" ), readPath( element.attribute( "project" ) ), brokenNodes, vLayerList );
695  continue;
696  }
697  else
698  {
699  if ( !addLayer( element, brokenNodes, vLayerList ) )
700  {
701  returnStatus = false;
702  }
703  }
704  emit layerLoaded( i + 1, nl.count() );
705  }
706 
707  //Update field map of layers with joins and create join caches if necessary
708  //Needs to be done here once all dependent layers are loaded
709  QList< QPair< QgsVectorLayer*, QDomElement > >::iterator vIt = vLayerList.begin();
710  for ( ; vIt != vLayerList.end(); ++vIt )
711  {
712  vIt->first->createJoinCaches();
713  vIt->first->updateFields();
714  }
715 
716  return qMakePair( returnStatus, brokenNodes );
717 
718 } // _getMapLayers
719 
720 
721 bool QgsProject::addLayer( const QDomElement& layerElem, QList<QDomNode>& brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList )
722 {
723  QString type = layerElem.attribute( "type" );
724  QgsDebugMsg( "Layer type is " + type );
725  QgsMapLayer *mapLayer = NULL;
726 
727  if ( type == "vector" )
728  {
729  mapLayer = new QgsVectorLayer;
730  }
731  else if ( type == "raster" )
732  {
733  mapLayer = new QgsRasterLayer;
734  }
735  else if ( type == "plugin" )
736  {
737  QString typeName = layerElem.attribute( "name" );
738  mapLayer = QgsPluginLayerRegistry::instance()->createLayer( typeName );
739  }
740 
741  if ( !mapLayer )
742  {
743  QgsDebugMsg( "Unable to create layer" );
744 
745  return false;
746  }
747 
748  Q_CHECK_PTR( mapLayer );
749 
750  // have the layer restore state that is stored in Dom node
751  if ( mapLayer->readLayerXML( layerElem ) && mapLayer->isValid() )
752  {
753  emit readMapLayer( mapLayer, layerElem );
754 
755  QList<QgsMapLayer *> myLayers;
756  myLayers << mapLayer;
758  QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
759  if ( vLayer && vLayer->vectorJoins().size() > 0 )
760  {
761  vectorLayerList.push_back( qMakePair( vLayer, layerElem ) );
762  }
763  return true;
764  }
765  else
766  {
767  delete mapLayer;
768 
769  QgsDebugMsg( "Unable to load " + type + " layer" );
770  brokenNodes.push_back( layerElem );
771  return false;
772  }
773 }
774 
775 
779 bool QgsProject::read( QFileInfo const &file )
780 {
781  imp_->file.setFileName( file.filePath() );
782 
783  return read();
784 } // QgsProject::read
785 
786 
787 
792 {
793  clearError();
794 
795  std::auto_ptr< QDomDocument > doc =
796  std::auto_ptr < QDomDocument > ( new QDomDocument( "qgis" ) );
797 
798  if ( !imp_->file.open( QIODevice::ReadOnly | QIODevice::Text ) )
799  {
800  imp_->file.close();
801 
802  setError( tr( "Unable to open %1" ).arg( imp_->file.fileName() ) );
803 
804  return false;
805  }
806 
807  // location of problem associated with errorMsg
808  int line, column;
809  QString errorMsg;
810 
811  if ( !doc->setContent( &imp_->file, &errorMsg, &line, &column ) )
812  {
813  // want to make this class as GUI independent as possible; so commented out
814 #if 0
815  QMessageBox::critical( 0, tr( "Project File Read Error" ),
816  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
817 #endif
818 
819  QString errorString = tr( "Project file read error: %1 at line %2 column %3" )
820  .arg( errorMsg ).arg( line ).arg( column );
821 
822  QgsDebugMsg( errorString );
823 
824  imp_->file.close();
825 
826  setError( tr( "%1 for file %2" ).arg( errorString ).arg( imp_->file.fileName() ) );
827 
828  return false;
829  }
830 
831  imp_->file.close();
832 
833 
834  QgsDebugMsg( "Opened document " + imp_->file.fileName() );
835  QgsDebugMsg( "Project title: " + imp_->title );
836 
837  // get project version string, if any
838  QgsProjectVersion fileVersion = _getVersion( *doc );
839  QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
840 
841  if ( thisVersion > fileVersion )
842  {
843  QgsLogger::warning( "Loading a file that was saved with an older "
844  "version of qgis (saved in " + fileVersion.text() +
845  ", loaded in " + QGis::QGIS_VERSION +
846  "). Problems may occur." );
847 
848  QgsProjectFileTransform projectFile( *doc, fileVersion );
849 
851  emit oldProjectVersionWarning( fileVersion.text() );
852  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
853 
854  projectFile.dump();
855 
856  projectFile.updateRevision( thisVersion );
857 
858  projectFile.dump();
859 
860  }
861 
862  // before we start loading everything, let's clear out the current set of
863  // properties first so that we don't have the properties from the previous
864  // project still hanging around
865 
866  imp_->clear();
867  mEmbeddedLayers.clear();
868 
869  // now get any properties
870  _getProperties( *doc, imp_->properties_ );
871 
872  QgsDebugMsg( QString::number( imp_->properties_.count() ) + " properties read" );
873 
874  dump_( imp_->properties_ );
875 
876 
877  // restore the canvas' area of interest
878 
879  // now get project title
880  _getTitle( *doc, imp_->title );
881 
882 
883  // get the map layers
884  QPair< bool, QList<QDomNode> > getMapLayersResults = _getMapLayers( *doc );
885 
886  // review the integrity of the retrieved map layers
887  bool clean = getMapLayersResults.first;
888 
889  if ( !clean )
890  {
891  QgsDebugMsg( "Unable to get map layers from project file." );
892 
893  if ( ! getMapLayersResults.second.isEmpty() )
894  {
895  QgsDebugMsg( "there are " + QString::number( getMapLayersResults.second.size() ) + " broken layers" );
896  }
897 
898  // we let a custom handler to decide what to do with missing layers
899  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
900  mBadLayerHandler->handleBadLayers( getMapLayersResults.second, *doc );
901  }
902 
903  // read the project: used by map canvas and legend
904  emit readProject( *doc );
905 
906  // if all went well, we're allegedly in pristine state
907  if ( clean )
908  dirty( false );
909 
910  return true;
911 
912 } // QgsProject::read
913 
914 
915 
916 
917 
918 bool QgsProject::read( QDomNode & layerNode )
919 {
920  QList<QDomNode> brokenNodes;
921  QList< QPair< QgsVectorLayer*, QDomElement > > vectorLayerList;
922  return addLayer( layerNode.toElement(), brokenNodes, vectorLayerList );
923 } // QgsProject::read( QDomNode & layerNode )
924 
925 
926 
927 bool QgsProject::write( QFileInfo const &file )
928 {
929  imp_->file.setFileName( file.filePath() );
930 
931  return write();
932 } // QgsProject::write( QFileInfo const & file )
933 
934 
936 {
937  clearError();
938 
939  // if we have problems creating or otherwise writing to the project file,
940  // let's find out up front before we go through all the hand-waving
941  // necessary to create all the Dom objects
942  if ( !imp_->file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
943  {
944  imp_->file.close(); // even though we got an error, let's make
945  // sure it's closed anyway
946 
947  setError( tr( "Unable to save to file %1" ).arg( imp_->file.fileName() ) );
948  return false;
949  }
950  QFileInfo myFileInfo( imp_->file );
951  if ( !myFileInfo.isWritable() )
952  {
953  // even though we got an error, let's make
954  // sure it's closed anyway
955  imp_->file.close();
956  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
957  .arg( imp_->file.fileName() ) );
958  return false;
959  }
960 
961 
962 
963  QDomImplementation DomImplementation;
964  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
965 
966  QDomDocumentType documentType =
967  DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd",
968  "SYSTEM" );
969  std::auto_ptr < QDomDocument > doc =
970  std::auto_ptr < QDomDocument > ( new QDomDocument( documentType ) );
971 
972 
973  QDomElement qgisNode = doc->createElement( "qgis" );
974  qgisNode.setAttribute( "projectname", title() );
975  qgisNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
976 
977  doc->appendChild( qgisNode );
978 
979  // title
980  QDomElement titleNode = doc->createElement( "title" );
981  qgisNode.appendChild( titleNode );
982 
983  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
984  titleNode.appendChild( titleText );
985 
986  // let map canvas and legend write their information
987  emit writeProject( *doc );
988 
989  // within top level node save list of layers
990  const QMap<QString, QgsMapLayer*> & layers = QgsMapLayerRegistry::instance()->mapLayers();
991 
992  // Iterate over layers in zOrder
993  // Call writeXML() on each
994  QDomElement projectLayersNode = doc->createElement( "projectlayers" );
995  projectLayersNode.setAttribute( "layercount", qulonglong( layers.size() ) );
996 
997  QMap<QString, QgsMapLayer*>::ConstIterator li = layers.constBegin();
998  while ( li != layers.end() )
999  {
1000  //QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer(*li);
1001  QgsMapLayer* ml = li.value();
1002 
1003  if ( ml )
1004  {
1005  QString externalProjectFile = layerIsEmbedded( ml->id() );
1006  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.find( ml->id() );
1007  if ( emIt == mEmbeddedLayers.constEnd() )
1008  {
1009  // general layer metadata
1010  QDomElement maplayerElem = doc->createElement( "maplayer" );
1011 
1012  ml->writeLayerXML( maplayerElem, *doc );
1013 
1014  emit writeMapLayer( ml, maplayerElem, *doc );
1015 
1016  projectLayersNode.appendChild( maplayerElem );
1017  }
1018  else //layer defined in an external project file
1019  {
1020  //only save embedded layer if not managed by a legend group
1021  if ( emIt.value().second )
1022  {
1023  QDomElement mapLayerElem = doc->createElement( "maplayer" );
1024  mapLayerElem.setAttribute( "embedded", 1 );
1025  mapLayerElem.setAttribute( "project", writePath( emIt.value().first ) );
1026  mapLayerElem.setAttribute( "id", ml->id() );
1027  projectLayersNode.appendChild( mapLayerElem );
1028  }
1029  }
1030  }
1031  li++;
1032  }
1033 
1034  qgisNode.appendChild( projectLayersNode );
1035 
1036  // now add the optional extra properties
1037 
1038  dump_( imp_->properties_ );
1039 
1040  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( imp_->properties_.count() ) ) );
1041 
1042  if ( !imp_->properties_.isEmpty() ) // only worry about properties if we
1043  // actually have any properties
1044  {
1045  imp_->properties_.writeXML( "properties", qgisNode, *doc );
1046  }
1047 
1048  // now wrap it up and ship it to the project file
1049  doc->normalize(); // XXX I'm not entirely sure what this does
1050 
1051  //QString xml = doc->toString(4); // write to string with indentation of four characters
1052  // (yes, four is arbitrary)
1053 
1054  // const char * xmlString = xml; // debugger probe point
1055  // qDebug( "project file output:\n\n" + xml );
1056 
1057  QTextStream projectFileStream( &imp_->file );
1058 
1059  //projectFileStream << xml << endl;
1060  doc->save( projectFileStream, 4 ); // save as utf-8
1061  imp_->file.close();
1062 
1063  // check if the text stream had no error - if it does
1064  // the user will get a message so they can try to resolve the
1065  // situation e.g. by saving project to a volume with more space
1066  //
1067  if ( projectFileStream.pos() == -1 || imp_->file.error() != QFile::NoError )
1068  {
1069  setError( tr( "Unable to save to file %1. Your project "
1070  "may be corrupted on disk. Try clearing some space on the volume and "
1071  "check file permissions before pressing save again." )
1072  .arg( imp_->file.fileName() ) );
1073  return false;
1074  }
1075 
1076  dirty( false ); // reset to pristine state
1077 
1078  emit projectSaved();
1079 
1080  return true;
1081 } // QgsProject::write
1082 
1083 
1084 
1086 {
1087  //QgsDebugMsg("entered.");
1088 
1089  imp_->clear();
1090 
1091  dirty( true );
1092 } // QgsProject::clearProperties()
1093 
1094 
1095 
1096 bool
1097 QgsProject::writeEntry( QString const &scope, const QString & key, bool value )
1098 {
1099  dirty( true );
1100 
1101  return addKey_( scope, key, &imp_->properties_, value );
1102 } // QgsProject::writeEntry ( ..., bool value )
1103 
1104 
1105 bool
1106 QgsProject::writeEntry( QString const &scope, const QString & key,
1107  double value )
1108 {
1109  dirty( true );
1110 
1111  return addKey_( scope, key, &imp_->properties_, value );
1112 } // QgsProject::writeEntry ( ..., double value )
1113 
1114 
1115 bool
1116 QgsProject::writeEntry( QString const &scope, const QString & key, int value )
1117 {
1118  dirty( true );
1119 
1120  return addKey_( scope, key, &imp_->properties_, value );
1121 } // QgsProject::writeEntry ( ..., int value )
1122 
1123 
1124 bool
1125 QgsProject::writeEntry( QString const &scope, const QString & key,
1126  const QString & value )
1127 {
1128  dirty( true );
1129 
1130  return addKey_( scope, key, &imp_->properties_, value );
1131 } // QgsProject::writeEntry ( ..., const QString & value )
1132 
1133 
1134 bool
1135 QgsProject::writeEntry( QString const &scope, const QString & key,
1136  const QStringList & value )
1137 {
1138  dirty( true );
1139 
1140  return addKey_( scope, key, &imp_->properties_, value );
1141 } // QgsProject::writeEntry ( ..., const QStringList & value )
1142 
1143 
1144 
1145 
1146 QStringList
1147 QgsProject::readListEntry( QString const & scope,
1148  const QString & key,
1149  QStringList def,
1150  bool * ok ) const
1151 {
1152  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1153 
1154  QVariant value;
1155 
1156  if ( property )
1157  {
1158  value = property->value();
1159  }
1160 
1161  bool valid = QVariant::StringList == value.type();
1162 
1163  if ( ok )
1164  {
1165  *ok = valid;
1166  }
1167 
1168  if ( valid )
1169  {
1170  return value.toStringList();
1171  }
1172 
1173  return def;
1174 } // QgsProject::readListEntry
1175 
1176 
1177 QString
1178 QgsProject::readEntry( QString const & scope,
1179  const QString & key,
1180  const QString & def,
1181  bool * ok ) const
1182 {
1183  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1184 
1185  QVariant value;
1186 
1187  if ( property )
1188  {
1189  value = property->value();
1190  }
1191 
1192  bool valid = value.canConvert( QVariant::String );
1193 
1194  if ( ok )
1195  {
1196  *ok = valid;
1197  }
1198 
1199  if ( valid )
1200  {
1201  return value.toString();
1202  }
1203 
1204  return QString( def );
1205 } // QgsProject::readEntry
1206 
1207 
1208 int
1209 QgsProject::readNumEntry( QString const &scope, const QString & key, int def,
1210  bool * ok ) const
1211 {
1212  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1213 
1214  QVariant value;
1215 
1216  if ( property )
1217  {
1218  value = property->value();
1219  }
1220 
1221  bool valid = value.canConvert( QVariant::String );
1222 
1223  if ( ok )
1224  {
1225  *ok = valid;
1226  }
1227 
1228  if ( valid )
1229  {
1230  return value.toInt();
1231  }
1232 
1233  return def;
1234 } // QgsProject::readNumEntry
1235 
1236 
1237 double
1238 QgsProject::readDoubleEntry( QString const &scope, const QString & key,
1239  double def,
1240  bool * ok ) const
1241 {
1242  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1243 
1244  QVariant value;
1245 
1246  if ( property )
1247  {
1248  value = property->value();
1249  }
1250 
1251  bool valid = value.canConvert( QVariant::Double );
1252 
1253  if ( ok )
1254  {
1255  *ok = valid;
1256  }
1257 
1258  if ( valid )
1259  {
1260  return value.toDouble();
1261  }
1262 
1263  return def;
1264 } // QgsProject::readDoubleEntry
1265 
1266 
1267 bool
1268 QgsProject::readBoolEntry( QString const &scope, const QString & key, bool def,
1269  bool * ok ) const
1270 {
1271  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1272 
1273  QVariant value;
1274 
1275  if ( property )
1276  {
1277  value = property->value();
1278  }
1279 
1280  bool valid = value.canConvert( QVariant::Bool );
1281 
1282  if ( ok )
1283  {
1284  *ok = valid;
1285  }
1286 
1287  if ( valid )
1288  {
1289  return value.toBool();
1290  }
1291 
1292  return def;
1293 } // QgsProject::readBoolEntry
1294 
1295 
1296 bool QgsProject::removeEntry( QString const &scope, const QString & key )
1297 {
1298  removeKey_( scope, key, imp_->properties_ );
1299 
1300  dirty( true );
1301 
1302  return ! findKey_( scope, key, imp_->properties_ );
1303 } // QgsProject::removeEntry
1304 
1305 
1306 
1307 QStringList QgsProject::entryList( QString const &scope, QString const &key ) const
1308 {
1309  QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1310 
1311  QStringList entries;
1312 
1313  if ( foundProperty )
1314  {
1315  QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1316 
1317  if ( propertyKey )
1318  { propertyKey->entryList( entries ); }
1319  }
1320 
1321  return entries;
1322 } // QgsProject::entryList
1323 
1324 
1325 QStringList
1326 QgsProject::subkeyList( QString const &scope, QString const &key ) const
1327 {
1328  QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1329 
1330  QStringList entries;
1331 
1332  if ( foundProperty )
1333  {
1334  QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1335 
1336  if ( propertyKey )
1337  { propertyKey->subkeyList( entries ); }
1338  }
1339 
1340  return entries;
1341 
1342 } // QgsProject::subkeyList
1343 
1344 
1345 
1347 {
1348  dump_( imp_->properties_ );
1349 } // QgsProject::dumpProperties
1350 
1351 
1352 // return the absolute path from a filename read from project file
1353 QString QgsProject::readPath( QString src ) const
1354 {
1355  if ( readBoolEntry( "Paths", "/Absolute", false ) )
1356  {
1357  return src;
1358  }
1359 
1360  // relative path should always start with ./ or ../
1361  if ( !src.startsWith( "./" ) && !src.startsWith( "../" ) )
1362  {
1363 #if defined(Q_OS_WIN)
1364  if ( src.startsWith( "\\\\" ) ||
1365  src.startsWith( "//" ) ||
1366  ( src[0].isLetter() && src[1] == ':' ) )
1367  {
1368  // UNC or absolute path
1369  return src;
1370  }
1371 #else
1372  if ( src[0] == '/' )
1373  {
1374  // absolute path
1375  return src;
1376  }
1377 #endif
1378 
1379  // so this one isn't absolute, but also doesn't start // with ./ or ../.
1380  // That means that it was saved with an earlier version of "relative path support",
1381  // where the source file had to exist and only the project directory was stripped
1382  // from the filename.
1383  QString home = homePath();
1384  if ( home.isNull() )
1385  return src;
1386 
1387  QFileInfo fi( home + "/" + src );
1388 
1389  if ( !fi.exists() )
1390  {
1391  return src;
1392  }
1393  else
1394  {
1395  return fi.canonicalFilePath();
1396  }
1397  }
1398 
1399  QString srcPath = src;
1400  QString projPath = fileName();
1401 
1402  if ( projPath.isEmpty() )
1403  {
1404  return src;
1405  }
1406 
1407 #if defined(Q_OS_WIN)
1408  srcPath.replace( "\\", "/" );
1409  projPath.replace( "\\", "/" );
1410 
1411  bool uncPath = projPath.startsWith( "//" );
1412 #endif
1413 
1414  QStringList srcElems = srcPath.split( "/", QString::SkipEmptyParts );
1415  QStringList projElems = projPath.split( "/", QString::SkipEmptyParts );
1416 
1417 #if defined(Q_OS_WIN)
1418  if ( uncPath )
1419  {
1420  projElems.insert( 0, "" );
1421  projElems.insert( 0, "" );
1422  }
1423 #endif
1424 
1425  // remove project file element
1426  projElems.removeLast();
1427 
1428  // append source path elements
1429  projElems << srcElems;
1430  projElems.removeAll( "." );
1431 
1432  // resolve ..
1433  int pos;
1434  while (( pos = projElems.indexOf( ".." ) ) > 0 )
1435  {
1436  // remove preceding element and ..
1437  projElems.removeAt( pos - 1 );
1438  projElems.removeAt( pos - 1 );
1439  }
1440 
1441 #if !defined(Q_OS_WIN)
1442  // make path absolute
1443  projElems.prepend( "" );
1444 #endif
1445 
1446  return projElems.join( "/" );
1447 }
1448 
1449 // return the absolute or relative path to write it to the project file
1450 QString QgsProject::writePath( QString src ) const
1451 {
1452  if ( readBoolEntry( "Paths", "/Absolute", false ) || src.isEmpty() )
1453  {
1454  return src;
1455  }
1456 
1457  QString srcPath = src;
1458  QString projPath = fileName();
1459 
1460  if ( projPath.isEmpty() )
1461  {
1462  return src;
1463  }
1464 
1465 #if defined( Q_OS_WIN )
1466  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1467 
1468  srcPath.replace( "\\", "/" );
1469 
1470  if ( srcPath.startsWith( "//" ) )
1471  {
1472  // keep UNC prefix
1473  srcPath = "\\\\" + srcPath.mid( 2 );
1474  }
1475 
1476  projPath.replace( "\\", "/" );
1477  if ( projPath.startsWith( "//" ) )
1478  {
1479  // keep UNC prefix
1480  projPath = "\\\\" + projPath.mid( 2 );
1481  }
1482 #else
1483  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1484 #endif
1485 
1486  QStringList projElems = projPath.split( "/", QString::SkipEmptyParts );
1487  QStringList srcElems = srcPath.split( "/", QString::SkipEmptyParts );
1488 
1489  // remove project file element
1490  projElems.removeLast();
1491 
1492  projElems.removeAll( "." );
1493  srcElems.removeAll( "." );
1494 
1495  // remove common part
1496  int n = 0;
1497  while ( srcElems.size() > 0 &&
1498  projElems.size() > 0 &&
1499  srcElems[0].compare( projElems[0], cs ) == 0 )
1500  {
1501  srcElems.removeFirst();
1502  projElems.removeFirst();
1503  n++;
1504  }
1505 
1506  if ( n == 0 )
1507  {
1508  // no common parts; might not even by a file
1509  return src;
1510  }
1511 
1512  if ( projElems.size() > 0 )
1513  {
1514  // go up to the common directory
1515  for ( int i = 0; i < projElems.size(); i++ )
1516  {
1517  srcElems.insert( 0, ".." );
1518  }
1519  }
1520  else
1521  {
1522  // let it start with . nevertheless,
1523  // so relative path always start with either ./ or ../
1524  srcElems.insert( 0, "." );
1525  }
1526 
1527  return srcElems.join( "/" );
1528 }
1529 
1530 void QgsProject::setError( QString errorMessage )
1531 {
1532  mErrorMessage = errorMessage;
1533 }
1534 
1535 QString QgsProject::error() const
1536 {
1537  return mErrorMessage;
1538 }
1539 
1541 {
1542  setError( QString() );
1543 }
1544 
1546 {
1547  delete mBadLayerHandler;
1548  mBadLayerHandler = handler;
1549 }
1550 
1551 QString QgsProject::layerIsEmbedded( const QString& id ) const
1552 {
1553  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1554  if ( it == mEmbeddedLayers.constEnd() )
1555  {
1556  return QString();
1557  }
1558  return it.value().first;
1559 }
1560 
1561 bool QgsProject::createEmbeddedLayer( const QString& layerId, const QString& projectFilePath, QList<QDomNode>& brokenNodes,
1562  QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList, bool saveFlag )
1563 {
1564  QFile projectFile( projectFilePath );
1565  if ( !projectFile.open( QIODevice::ReadOnly ) )
1566  {
1567  return false;
1568  }
1569 
1570  QDomDocument projectDocument;
1571  if ( !projectDocument.setContent( &projectFile ) )
1572  {
1573  return false;
1574  }
1575 
1576  //does project store pathes absolute or relative?
1577  bool useAbsolutePathes = true;
1578  QDomElement propertiesElem = projectDocument.documentElement().firstChildElement( "properties" );
1579  if ( !propertiesElem.isNull() )
1580  {
1581  QDomElement absElem = propertiesElem.firstChildElement( "Paths" ).firstChildElement( "Absolute" );
1582  if ( !absElem.isNull() )
1583  {
1584  useAbsolutePathes = absElem.text().compare( "true", Qt::CaseInsensitive ) == 0;
1585  }
1586  }
1587 
1588  QDomElement projectLayersElem = projectDocument.documentElement().firstChildElement( "projectlayers" );
1589  if ( projectLayersElem.isNull() )
1590  {
1591  return false;
1592  }
1593 
1594  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( "maplayer" );
1595  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1596  {
1597  //get layer id
1598  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1599  QString id = mapLayerElem.firstChildElement( "id" ).text();
1600  if ( id == layerId )
1601  {
1602  //layer can be embedded only once
1603  if ( mapLayerElem.attribute( "embedded" ) == "1" )
1604  {
1605  return false;
1606  }
1607 
1608  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1609 
1610  //change datasource path from relative to absolute if necessary
1611  if ( !useAbsolutePathes )
1612  {
1613  QDomElement provider = mapLayerElem.firstChildElement( "provider" );
1614  if ( provider.text() == "spatialite" )
1615  {
1616  QDomElement dsElem = mapLayerElem.firstChildElement( "datasource" );
1617 
1618  QgsDataSourceURI uri( dsElem.text() );
1619 
1620  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + "/" + uri.database() );
1621  if ( absoluteDs.exists() )
1622  {
1623  uri.setDatabase( absoluteDs.absoluteFilePath() );
1624  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1625  dsElem.appendChild( projectDocument.createTextNode( uri.uri() ) );
1626  }
1627  }
1628  else
1629  {
1630  QDomElement dsElem = mapLayerElem.firstChildElement( "datasource" );
1631  QString debug( QFileInfo( projectFilePath ).absolutePath() + "/" + dsElem.text() );
1632  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + "/" + dsElem.text() );
1633  if ( absoluteDs.exists() )
1634  {
1635  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1636  dsElem.appendChild( projectDocument.createTextNode( absoluteDs.absoluteFilePath() ) );
1637  }
1638  }
1639  }
1640 
1641  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1642  {
1643  return true;
1644  }
1645  else
1646  {
1647  mEmbeddedLayers.remove( layerId );
1648  return false;
1649  }
1650  }
1651  }
1652 
1653  return false;
1654 }
1655 
1656 void QgsProject::setSnapSettingsForLayer( const QString& layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
1657 {
1658  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1659  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1660  int idx = layerIdList.indexOf( layerId );
1661  if ( idx != -1 )
1662  {
1663  layerIdList.removeAt( idx );
1664  enabledList.removeAt( idx );
1665  snapTypeList.removeAt( idx );
1666  toleranceUnitList.removeAt( idx );
1667  toleranceList.removeAt( idx );
1668  avoidIntersectionList.removeOne( layerId );
1669  }
1670 
1671  layerIdList.append( layerId );
1672 
1673  //enabled
1674  enabledList.append( enabled ? "enabled" : "disabled" );
1675 
1676  //snap type
1677  QString typeString;
1678  if ( type == QgsSnapper::SnapToSegment )
1679  {
1680  typeString = "to_segment";
1681  }
1682  else if ( type == QgsSnapper::SnapToVertexAndSegment )
1683  {
1684  typeString = "to_vertex_and_segment";
1685  }
1686  else
1687  {
1688  typeString = "to_vertex";
1689  }
1690  snapTypeList.append( typeString );
1691 
1692  //units
1693  toleranceUnitList.append( unit == QgsTolerance::Pixels ? "1" : "0" );
1694 
1695  //tolerance
1696  toleranceList.append( QString::number( tolerance ) );
1697 
1698  //avoid intersection
1699  if ( avoidIntersection )
1700  {
1701  avoidIntersectionList.append( layerId );
1702  }
1703 
1704  writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
1705  writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
1706  writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
1707  writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
1708  writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
1709  writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
1710  emit snapSettingsChanged();
1711 }
1712 
1713 bool QgsProject::snapSettingsForLayer( const QString& layerId, bool& enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType& units, double& tolerance,
1714  bool& avoidIntersection ) const
1715 {
1716  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1717  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1718  int idx = layerIdList.indexOf( layerId );
1719  if ( idx == -1 )
1720  {
1721  return false;
1722  }
1723 
1724  //make sure all lists are long enough
1725  int minListEntries = idx + 1;
1726  if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
1727  toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
1728  {
1729  return false;
1730  }
1731 
1732  //enabled
1733  enabled = enabledList.at( idx ) == "enabled";
1734 
1735  //snap type
1736  QString snapType = snapTypeList.at( idx );
1737  if ( snapType == "to_segment" )
1738  {
1740  }
1741  else if ( snapType == "to_vertex_and_segment" )
1742  {
1744  }
1745  else //to vertex
1746  {
1747  type = QgsSnapper::SnapToVertex;
1748  }
1749 
1750  //units
1751  if ( toleranceUnitList.at( idx ) == "1" )
1752  {
1753  units = QgsTolerance::Pixels;
1754  }
1755  else
1756  {
1757  units = QgsTolerance::MapUnits;
1758  }
1759 
1760  //tolerance
1761  tolerance = toleranceList.at( idx ).toDouble();
1762 
1763  //avoid intersection
1764  avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
1765 
1766  return true;
1767 }
1768 
1769 void QgsProject::snapSettings( QStringList& layerIdList, QStringList& enabledList, QStringList& snapTypeList, QStringList& toleranceUnitList, QStringList& toleranceList,
1770  QStringList& avoidIntersectionList ) const
1771 {
1772  layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
1773  enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
1774  toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
1775  toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
1776  snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
1777  avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
1778 }
1779 
1781 {
1782  QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
1783  emit snapSettingsChanged();
1784 }
1785 
1787 {
1788  return ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) > 0 );
1789 }
1790 
1791 void QgsProjectBadLayerDefaultHandler::handleBadLayers( QList<QDomNode> /*layers*/, QDomDocument /*projectDom*/ )
1792 {
1793  // just ignore any bad layers
1794 }
1795 
1796 QString QgsProject::homePath() const
1797 {
1798  QFileInfo pfi( fileName() );
1799  if ( !pfi.exists() )
1800  return QString::null;
1801 
1802  return pfi.canonicalPath();
1803 }
QString mErrorMessage
Definition: qgsproject.h:389
static const char * QGIS_VERSION
Definition: qgis.h:40
const QList< QgsVectorJoinInfo > & vectorJoins() const
bool topologicalEditing() const
Convenience function to query topological editing status.
static QgsProperty * findKey_(QString const &scope, QString const &key, QgsPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:82
void snapSettings(QStringList &layerIdList, QStringList &enabledList, QStringList &snapTypeList, QStringList &snapUnitList, QStringList &toleranceUnitList, QStringList &avoidIntersectionList) const
Base class for all map layer types.
Definition: qgsmaplayer.h:45
QgsPluginLayer * createLayer(QString typeName)
return new layer if corresponding plugin has been found, else return NULL
QHash< QString, QPair< QString, bool > > mEmbeddedLayers
Embeded layers which are defined in other projects.
Definition: qgsproject.h:396
QgsPropertyKey properties_
property hierarchy
Definition: qgsproject.cpp:297
QgsPropertyKey * addKey(const QString &keyName)
add the given property key
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
bool snapSettingsForLayer(const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance, bool &avoidIntersection) const
Convenience function to query snap settings of a layer.
void entryList(QStringList &entries) const
return keys that do not contain other keys
static void _getProperties(QDomDocument const &doc, QgsPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:457
QgsPropertyValue * setValue(const QString &name, const QVariant &value)
set the value associated with this key
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
static QgsProperty * addKey_(QString const &scope, QString const &key, QgsPropertyKey *rootProperty, QVariant value)
add the given key and value
Definition: qgsproject.cpp:156
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:162
void oldProjectVersionWarning(QString)
emitted when an old project file is read.
QPair< bool, QList< QDomNode > > _getMapLayers(QDomDocument const &doc)
Read map layers from project file.
Definition: qgsproject.cpp:647
void setDatabase(const QString &database)
Set database.
void setFileName(const QString &name)
Every project has an associated file that contains its XML.
Definition: qgsproject.cpp:398
UnitType
Type of unit of tolerance value from settings.
Definition: qgstolerance.h:33
bool dirty
true if project has been modified since it has been read or saved
Definition: qgsproject.cpp:303
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.
QString homePath() const
Return project's home path.
void clear()
clear project properties when a new project is started
Definition: qgsproject.cpp:316
static void _getTitle(QDomDocument const &doc, QString &title)
Get the project title.
Definition: qgsproject.cpp:542
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=0) const
void projectSaved()
emitted when the project file has been written and closed
QString readPath(QString filename) const
turn filename read from the project file to an absolute path
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=0) const
SnappingType
Snap to vertex, to segment or both.
Definition: qgssnapper.h:65
bool writeEntry(const QString &scope, const QString &key, bool value)
QStringList readListEntry(const QString &scope, const QString &key, QStringList def=QStringList(), bool *ok=0) const
key value accessors
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList, bool saveFlag=true)
Creates a maplayer instance defined in an arbitrary project file.
bool read()
presuming that the caller has already reset the map canvas, map registry, and legend ...
Definition: qgsproject.cpp:791
static QgsProjectVersion _getVersion(QDomDocument const &doc)
return the version string found in the given Dom document
Definition: qgsproject.cpp:581
void removeKey(const QString &keyName)
remove the given key
QStringList subkeyList(const QString &scope, const QString &key) const
return keys with keys – do not return keys that contain only values
bool writeLayerXML(QDomElement &layerElement, QDomDocument &document)
stores state in Dom node
Pixels unit of tolerance.
Definition: qgstolerance.h:38
QList< QgsMapLayer * > addMapLayers(QList< QgsMapLayer * > theMapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
virtual void clearKeys()
delete any sub-nodes
Map unit value.
Definition: qgstolerance.h:36
A class to describe the version of a project.
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
void readProject(const QDomDocument &)
emitted when project is being read
QgsPropertyKey node.
void layerLoaded(int i, int n)
emitted when a layer from a projects was read
QString writePath(QString filename) const
prepare a filename to save it to the project file
QString fileName() const
returns file name
Definition: qgsproject.cpp:407
QString id() const
Get this layer's unique ID, this ID is used to access this layer from map layer registry.
Definition: qgsmaplayer.cpp:95
bool removeEntry(const QString &scope, const QString &key)
remove the given key
Reads and writes project states.
Definition: qgsproject.h:64
bool readXML(QDomNode &keyNode)
restores property hierarchy to given Dom node
QFile file
current physical project file
Definition: qgsproject.cpp:294
void dump(size_t tabs=0) const
dumps out the keys and values
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.
bool isValid()
An Abstract Base Class for QGIS project property hierarchies.
QgsProjectBadLayerHandler * mBadLayerHandler
Definition: qgsproject.h:391
virtual void handleBadLayers(QList< QDomNode > layers, QDomDocument projectDom)
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=0) const
QgsProperty * find(QString &propertyName)
bool write()
Definition: qgsproject.cpp:935
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
void subkeyList(QStringList &entries) const
return keys that contain other keys
void clearError()
Clear error message.
void setError(QString errorMessage)
Set error message from read/write operation.
bool isDirty() const
the dirty flag is true if the project has been modified since the last write()
Definition: qgsproject.cpp:385
QString file
Definition: qgssvgcache.cpp:74
bool addLayer(const QDomElement &layerElem, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList)
Definition: qgsproject.cpp:721
void writeProject(QDomDocument &)
emitted when project is being written
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=0) const
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
static QgsProject * theProject_
Definition: qgsproject.h:385
std::auto_ptr< Imp > imp_
implementation handle
Definition: qgsproject.h:380
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:358
static void removeKey_(QString const &scope, QString const &key, QgsPropertyKey &rootProperty)
Definition: qgsproject.cpp:229
void loadingLayer(QString)
const QMap< QString, QgsMapLayer * > & mapLayers()
Retrieve the mapLayers collection (mainly intended for use by projection)
QStringList entryList(const QString &scope, const QString &key) const
return keys with values – do not return keys that contain other keys
bool readLayerXML(const QDomElement &layerElement)
sets state from Dom document
QString title
project title
Definition: qgsproject.cpp:300
const QString & title() const
returns title
Definition: qgsproject.cpp:379
void dumpProperties() const
dump out current project properties to stderr
void clearProperties()
syntactic sugar for property lists
static QgsPluginLayerRegistry * instance()
means of accessing canonical single instance
static QStringList makeKeyTokens_(QString const &scope, QString const &key)
Take the given scope and key and convert them to a string list of key tokens that will be used to nav...
Definition: qgsproject.cpp:54
const QString & name() const
every key has a name
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted, after the basic initialisation of a layer from the project file is done. ...
Default bad layer handler which ignores any missing layers.
Definition: qgsproject.h:416
virtual bool isKey() const =0
returns true if is a QgsPropertyKey
Represents a vector layer which manages a vector based data sets.
virtual bool isValue() const =0
returns true if is a QgsPropertyValue
static void dump_(QgsPropertyKey const &topQgsPropertyKey)
basically a debugging tool to dump property list values
Definition: qgsproject.cpp:415
Interface for classes that handle missing layer files when reading project file.
Definition: qgsproject.h:406
void snapSettingsChanged()
virtual void handleBadLayers(QList< QDomNode > layers, QDomDocument projectDom)=0
void dirty(bool b)
Definition: qgsproject.cpp:391
#define tr(sourceText)