QGIS API Documentation  2.3.0-Master
 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 "qgsdatasourceuri.h"
24 #include "qgsexception.h"
25 #include "qgslogger.h"
26 #include "qgsmaplayerregistry.h"
27 #include "qgspluginlayer.h"
28 #include "qgspluginlayerregistry.h"
30 #include "qgsprojectproperty.h"
31 #include "qgsprojectversion.h"
32 #include "qgsrasterlayer.h"
33 #include "qgsrectangle.h"
34 #include "qgsrelationmanager.h"
35 #include "qgsvectorlayer.h"
36 
37 #include <QApplication>
38 #include <QFileInfo>
39 #include <QDomNode>
40 #include <QObject>
41 #include <QTextStream>
42 
43 // canonical project instance
45 
54 static
55 QStringList makeKeyTokens_( QString const &scope, QString const &key )
56 {
57  // XXX - debugger probes
58  //const char * scope_str = scope.toLocal8Bit().data();
59  //const char * key_str = key.toLocal8Bit().data();
60 
61  QStringList keyTokens = QStringList( scope );
62  keyTokens += key.split( '/', QString::SkipEmptyParts );
63 
64  // be sure to include the canonical root node
65  keyTokens.push_front( "properties" );
66 
67  return keyTokens;
68 } // makeKeyTokens_
69 
70 
71 
72 
82 static
83 QgsProperty * findKey_( QString const & scope,
84  QString const & key,
85  QgsPropertyKey & rootProperty )
86 {
87  QgsPropertyKey * currentProperty = &rootProperty;
88  QgsProperty * nextProperty; // link to next property down hiearchy
89 
90  QStringList keySequence = makeKeyTokens_( scope, key );
91 
92  while ( ! keySequence.isEmpty() )
93  {
94  // if the current head of the sequence list matches the property name,
95  // then traverse down the property hierarchy
96  if ( keySequence.first() == currentProperty->name() )
97  {
98  // remove front key since we're traversing down a level
99  keySequence.pop_front();
100 
101  // if we have only one key name left, then return the key found
102  if ( 1 == keySequence.count() )
103  {
104  return currentProperty->find( keySequence.front() );
105 
106  }
107  // if we're out of keys then the current property is the one we
108  // want; i.e., we're in the rate case of being at the top-most
109  // property node
110  else if ( keySequence.isEmpty() )
111  {
112  return currentProperty;
113  }
114  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
115  {
116  if ( nextProperty->isKey() )
117  {
118  currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
119  }
120  // it may be that this may be one of several property value
121  // nodes keyed by QDict string; if this is the last remaining
122  // key token and the next property is a value node, then
123  // that's the situation, so return the currentProperty
124  else if ( nextProperty->isValue() && ( 1 == keySequence.count() ) )
125  {
126  return currentProperty;
127  }
128  else // QgsPropertyValue not Key, so return null
129  {
130  return 0x0;
131  }
132  }
133  else // if the next key down isn't found
134  { // then the overall key sequence doesn't exist
135  return 0x0;
136  }
137  }
138  else
139  {
140  return 0x0;
141  }
142  }
143 
144  return 0x0;
145 } // findKey_
146 
147 
148 
156 static
157 QgsProperty * addKey_( QString const & scope,
158  QString const & key,
159  QgsPropertyKey * rootProperty,
160  QVariant value )
161 {
162  QStringList keySequence = makeKeyTokens_( scope, key );
163 
164  // cursor through property key/value hierarchy
165  QgsPropertyKey * currentProperty = rootProperty;
166 
167  QgsProperty * newProperty; // link to next property down hiearchy
168 
169  while ( ! keySequence.isEmpty() )
170  {
171  // if the current head of the sequence list matches the property name,
172  // then traverse down the property hierarchy
173  if ( keySequence.first() == currentProperty->name() )
174  {
175  // remove front key since we're traversing down a level
176  keySequence.pop_front();
177 
178  // if key sequence has one last element, then we use that as the
179  // name to store the value
180  if ( 1 == keySequence.count() )
181  {
182  currentProperty->setValue( keySequence.front(), value );
183  return currentProperty;
184  }
185  // we're at the top element if popping the keySequence element
186  // will leave it empty; in that case, just add the key
187  else if ( keySequence.isEmpty() )
188  {
189  currentProperty->setValue( value );
190 
191  return currentProperty;
192  }
193  else if (( newProperty = currentProperty->find( keySequence.first() ) ) )
194  {
195  currentProperty = dynamic_cast<QgsPropertyKey*>( newProperty );
196 
197  if ( currentProperty )
198  {
199  continue;
200  }
201  else // QgsPropertyValue not Key, so return null
202  {
203  return 0x0;
204  }
205  }
206  else // the next subkey doesn't exist, so add it
207  {
208  newProperty = currentProperty->addKey( keySequence.first() );
209 
210  if ( newProperty )
211  {
212  currentProperty = dynamic_cast<QgsPropertyKey*>( newProperty );
213  }
214  continue;
215  }
216  }
217  else
218  {
219  return 0x0;
220  }
221  }
222 
223  return 0x0;
224 
225 } // addKey_
226 
227 
228 
229 static
230 void removeKey_( QString const & scope,
231  QString const & key,
232  QgsPropertyKey & rootProperty )
233 {
234  QgsPropertyKey * currentProperty = &rootProperty;
235 
236  QgsProperty * nextProperty = NULL; // link to next property down hiearchy
237  QgsPropertyKey * previousQgsPropertyKey = NULL; // link to previous property up hiearchy
238 
239  QStringList keySequence = makeKeyTokens_( scope, key );
240 
241  while ( ! keySequence.isEmpty() )
242  {
243  // if the current head of the sequence list matches the property name,
244  // then traverse down the property hierarchy
245  if ( keySequence.first() == currentProperty->name() )
246  {
247  // remove front key since we're traversing down a level
248  keySequence.pop_front();
249 
250  // if we have only one key name left, then try to remove the key
251  // with that name
252  if ( 1 == keySequence.count() )
253  {
254  currentProperty->removeKey( keySequence.front() );
255  }
256  // if we're out of keys then the current property is the one we
257  // want to remove, but we can't delete it directly; we need to
258  // delete it from the parent property key container
259  else if ( keySequence.isEmpty() )
260  {
261  previousQgsPropertyKey->removeKey( currentProperty->name() );
262  }
263  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
264  {
265  previousQgsPropertyKey = currentProperty;
266  currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
267 
268  if ( currentProperty )
269  {
270  continue;
271  }
272  else // QgsPropertyValue not Key, so return null
273  {
274  return;
275  }
276  }
277  else // if the next key down isn't found
278  { // then the overall key sequence doesn't exist
279  return;
280  }
281  }
282  else
283  {
284  return;
285  }
286  }
287 
288 } // void removeKey_
289 
290 
291 
293 {
295  QFile file;
296 
299 
301  QString title;
302 
304  bool dirty;
305 
306  Imp()
307  : title( "" )
308  , dirty( false )
309  { // top property node is the root
310  // "properties" that contains all plug-in
311  // and extra property keys and values
312  properties_.name() = "properties"; // root property node always this value
313  }
314 
317  void clear()
318  {
319  //QgsDebugMsg( "Clearing project properties Impl->clear();" );
320 
322  title = "";
323 
324  // reset some default project properties
325  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
326  QgsProject::instance()->writeEntry( "PositionPrecision", "/Automatic", true );
327  QgsProject::instance()->writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
328  QgsProject::instance()->writeEntry( "Paths", "/Absolute", false );
329  }
330 
331 }; // struct QgsProject::Imp
332 
333 
334 
336  : imp_( new QgsProject::Imp )
337  , mBadLayerHandler( new QgsProjectBadLayerDefaultHandler() )
338  , mRelationManager( new QgsRelationManager( this ) )
339 {
340  // Set some default project properties
341  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
342  writeEntry( "PositionPrecision", "/Automatic", true );
343  writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
344  writeEntry( "Paths", "/Absolute", false );
345  // XXX writeEntry() makes the project dirty, but it doesn't make sense
346  // for a new project to be dirty, so let's clean it up
347  dirty( false );
348 } // QgsProject ctor
349 
350 
351 
353 {
354  delete mBadLayerHandler;
355  delete mRelationManager;
356 
357  // note that std::auto_ptr automatically deletes imp_ when it's destroyed
358 } // QgsProject dtor
359 
360 
361 
363 {
364  if ( !theProject_ )
365  {
366  theProject_ = new QgsProject;
367  }
368  return theProject_;
369 } // QgsProject *instance()
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 bool QgsProject::addLayer( const QDomElement& layerElem, QList<QDomNode>& brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList )
721 {
722  QString type = layerElem.attribute( "type" );
723  QgsDebugMsg( "Layer type is " + type );
724  QgsMapLayer *mapLayer = NULL;
725 
726  if ( type == "vector" )
727  {
728  mapLayer = new QgsVectorLayer;
729  }
730  else if ( type == "raster" )
731  {
732  mapLayer = new QgsRasterLayer;
733  }
734  else if ( type == "plugin" )
735  {
736  QString typeName = layerElem.attribute( "name" );
737  mapLayer = QgsPluginLayerRegistry::instance()->createLayer( typeName );
738  }
739 
740  if ( !mapLayer )
741  {
742  QgsDebugMsg( "Unable to create layer" );
743 
744  return false;
745  }
746 
747  Q_CHECK_PTR( mapLayer );
748 
749  // have the layer restore state that is stored in Dom node
750  if ( mapLayer->readLayerXML( layerElem ) && mapLayer->isValid() )
751  {
752  emit readMapLayer( mapLayer, layerElem );
753 
754  QList<QgsMapLayer *> myLayers;
755  myLayers << mapLayer;
757  QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
758  if ( vLayer && vLayer->vectorJoins().size() > 0 )
759  {
760  vectorLayerList.push_back( qMakePair( vLayer, layerElem ) );
761  }
762  return true;
763  }
764  else
765  {
766  delete mapLayer;
767 
768  QgsDebugMsg( "Unable to load " + type + " layer" );
769  brokenNodes.push_back( layerElem );
770  return false;
771  }
772 }
773 
774 
778 bool QgsProject::read( QFileInfo const &file )
779 {
780  imp_->file.setFileName( file.filePath() );
781 
782  return read();
783 } // QgsProject::read
784 
785 
786 
791 {
792  clearError();
793 
794  std::auto_ptr< QDomDocument > doc =
795  std::auto_ptr < QDomDocument > ( new QDomDocument( "qgis" ) );
796 
797  if ( !imp_->file.open( QIODevice::ReadOnly | QIODevice::Text ) )
798  {
799  imp_->file.close();
800 
801  setError( tr( "Unable to open %1" ).arg( imp_->file.fileName() ) );
802 
803  return false;
804  }
805 
806  // location of problem associated with errorMsg
807  int line, column;
808  QString errorMsg;
809 
810  if ( !doc->setContent( &imp_->file, &errorMsg, &line, &column ) )
811  {
812  // want to make this class as GUI independent as possible; so commented out
813 #if 0
814  QMessageBox::critical( 0, tr( "Project File Read Error" ),
815  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
816 #endif
817 
818  QString errorString = tr( "Project file read error: %1 at line %2 column %3" )
819  .arg( errorMsg ).arg( line ).arg( column );
820 
821  QgsDebugMsg( errorString );
822 
823  imp_->file.close();
824 
825  setError( tr( "%1 for file %2" ).arg( errorString ).arg( imp_->file.fileName() ) );
826 
827  return false;
828  }
829 
830  imp_->file.close();
831 
832 
833  QgsDebugMsg( "Opened document " + imp_->file.fileName() );
834  QgsDebugMsg( "Project title: " + imp_->title );
835 
836  // get project version string, if any
837  QgsProjectVersion fileVersion = _getVersion( *doc );
838  QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
839 
840  if ( thisVersion > fileVersion )
841  {
842  QgsLogger::warning( "Loading a file that was saved with an older "
843  "version of qgis (saved in " + fileVersion.text() +
844  ", loaded in " + QGis::QGIS_VERSION +
845  "). Problems may occur." );
846 
847  QgsProjectFileTransform projectFile( *doc, fileVersion );
848 
850  emit oldProjectVersionWarning( fileVersion.text() );
851  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
852 
853  projectFile.dump();
854 
855  projectFile.updateRevision( thisVersion );
856 
857  projectFile.dump();
858 
859  }
860 
861  // before we start loading everything, let's clear out the current set of
862  // properties first so that we don't have the properties from the previous
863  // project still hanging around
864 
865  imp_->clear();
866  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  // Create backup file
940  if ( QFile::exists( fileName() ) )
941  {
942  QString backup = fileName() + "~";
943  if ( QFile::exists( backup ) )
944  QFile::remove( backup );
945  QFile::rename( fileName(), backup );
946  }
947 
948  // if we have problems creating or otherwise writing to the project file,
949  // let's find out up front before we go through all the hand-waving
950  // necessary to create all the Dom objects
951  if ( !imp_->file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
952  {
953  imp_->file.close(); // even though we got an error, let's make
954  // sure it's closed anyway
955 
956  setError( tr( "Unable to save to file %1" ).arg( imp_->file.fileName() ) );
957  return false;
958  }
959  QFileInfo myFileInfo( imp_->file );
960  if ( !myFileInfo.isWritable() )
961  {
962  // even though we got an error, let's make
963  // sure it's closed anyway
964  imp_->file.close();
965  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
966  .arg( imp_->file.fileName() ) );
967  return false;
968  }
969 
970 
971 
972  QDomImplementation DomImplementation;
973  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
974 
975  QDomDocumentType documentType =
976  DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd",
977  "SYSTEM" );
978  std::auto_ptr < QDomDocument > doc =
979  std::auto_ptr < QDomDocument > ( new QDomDocument( documentType ) );
980 
981 
982  QDomElement qgisNode = doc->createElement( "qgis" );
983  qgisNode.setAttribute( "projectname", title() );
984  qgisNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
985 
986  doc->appendChild( qgisNode );
987 
988  // title
989  QDomElement titleNode = doc->createElement( "title" );
990  qgisNode.appendChild( titleNode );
991 
992  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
993  titleNode.appendChild( titleText );
994 
995  // let map canvas and legend write their information
996  emit writeProject( *doc );
997 
998  // within top level node save list of layers
999  const QMap<QString, QgsMapLayer*> & layers = QgsMapLayerRegistry::instance()->mapLayers();
1000 
1001  // Iterate over layers in zOrder
1002  // Call writeXML() on each
1003  QDomElement projectLayersNode = doc->createElement( "projectlayers" );
1004  projectLayersNode.setAttribute( "layercount", qulonglong( layers.size() ) );
1005 
1006  QMap<QString, QgsMapLayer*>::ConstIterator li = layers.constBegin();
1007  while ( li != layers.end() )
1008  {
1009  //QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer(*li);
1010  QgsMapLayer* ml = li.value();
1011 
1012  if ( ml )
1013  {
1014  QString externalProjectFile = layerIsEmbedded( ml->id() );
1015  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.find( ml->id() );
1016  if ( emIt == mEmbeddedLayers.constEnd() )
1017  {
1018  // general layer metadata
1019  QDomElement maplayerElem = doc->createElement( "maplayer" );
1020 
1021  ml->writeLayerXML( maplayerElem, *doc );
1022 
1023  emit writeMapLayer( ml, maplayerElem, *doc );
1024 
1025  projectLayersNode.appendChild( maplayerElem );
1026  }
1027  else //layer defined in an external project file
1028  {
1029  //only save embedded layer if not managed by a legend group
1030  if ( emIt.value().second )
1031  {
1032  QDomElement mapLayerElem = doc->createElement( "maplayer" );
1033  mapLayerElem.setAttribute( "embedded", 1 );
1034  mapLayerElem.setAttribute( "project", writePath( emIt.value().first ) );
1035  mapLayerElem.setAttribute( "id", ml->id() );
1036  projectLayersNode.appendChild( mapLayerElem );
1037  }
1038  }
1039  }
1040  li++;
1041  }
1042 
1043  qgisNode.appendChild( projectLayersNode );
1044 
1045  // now add the optional extra properties
1046 
1047  dump_( imp_->properties_ );
1048 
1049  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( imp_->properties_.count() ) ) );
1050 
1051  if ( !imp_->properties_.isEmpty() ) // only worry about properties if we
1052  // actually have any properties
1053  {
1054  imp_->properties_.writeXML( "properties", qgisNode, *doc );
1055  }
1056 
1057  // now wrap it up and ship it to the project file
1058  doc->normalize(); // XXX I'm not entirely sure what this does
1059 
1060  //QString xml = doc->toString(4); // write to string with indentation of four characters
1061  // (yes, four is arbitrary)
1062 
1063  // const char * xmlString = xml; // debugger probe point
1064  // qDebug( "project file output:\n\n" + xml );
1065 
1066  QTextStream projectFileStream( &imp_->file );
1067 
1068  //projectFileStream << xml << endl;
1069  doc->save( projectFileStream, 4 ); // save as utf-8
1070  imp_->file.close();
1071 
1072  // check if the text stream had no error - if it does
1073  // the user will get a message so they can try to resolve the
1074  // situation e.g. by saving project to a volume with more space
1075  //
1076  if ( projectFileStream.pos() == -1 || imp_->file.error() != QFile::NoError )
1077  {
1078  setError( tr( "Unable to save to file %1. Your project "
1079  "may be corrupted on disk. Try clearing some space on the volume and "
1080  "check file permissions before pressing save again." )
1081  .arg( imp_->file.fileName() ) );
1082  return false;
1083  }
1084 
1085  dirty( false ); // reset to pristine state
1086 
1087  emit projectSaved();
1088 
1089  return true;
1090 } // QgsProject::write
1091 
1092 
1093 
1095 {
1096  //QgsDebugMsg("entered.");
1097 
1098  imp_->clear();
1099 
1100  dirty( true );
1101 } // QgsProject::clearProperties()
1102 
1103 
1104 
1105 bool
1106 QgsProject::writeEntry( QString const &scope, const QString & key, bool value )
1107 {
1108  dirty( true );
1109 
1110  return addKey_( scope, key, &imp_->properties_, value );
1111 } // QgsProject::writeEntry ( ..., bool value )
1112 
1113 
1114 bool
1115 QgsProject::writeEntry( QString const &scope, const QString & key,
1116  double value )
1117 {
1118  dirty( true );
1119 
1120  return addKey_( scope, key, &imp_->properties_, value );
1121 } // QgsProject::writeEntry ( ..., double value )
1122 
1123 
1124 bool
1125 QgsProject::writeEntry( QString const &scope, const QString & key, int value )
1126 {
1127  dirty( true );
1128 
1129  return addKey_( scope, key, &imp_->properties_, value );
1130 } // QgsProject::writeEntry ( ..., int value )
1131 
1132 
1133 bool
1134 QgsProject::writeEntry( QString const &scope, const QString & key,
1135  const QString & value )
1136 {
1137  dirty( true );
1138 
1139  return addKey_( scope, key, &imp_->properties_, value );
1140 } // QgsProject::writeEntry ( ..., const QString & value )
1141 
1142 
1143 bool
1144 QgsProject::writeEntry( QString const &scope, const QString & key,
1145  const QStringList & value )
1146 {
1147  dirty( true );
1148 
1149  return addKey_( scope, key, &imp_->properties_, value );
1150 } // QgsProject::writeEntry ( ..., const QStringList & value )
1151 
1152 
1153 
1154 
1155 QStringList
1156 QgsProject::readListEntry( QString const & scope,
1157  const QString & key,
1158  QStringList def,
1159  bool * ok ) const
1160 {
1161  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1162 
1163  QVariant value;
1164 
1165  if ( property )
1166  {
1167  value = property->value();
1168  }
1169 
1170  bool valid = QVariant::StringList == value.type();
1171 
1172  if ( ok )
1173  {
1174  *ok = valid;
1175  }
1176 
1177  if ( valid )
1178  {
1179  return value.toStringList();
1180  }
1181 
1182  return def;
1183 } // QgsProject::readListEntry
1184 
1185 
1186 QString
1187 QgsProject::readEntry( QString const & scope,
1188  const QString & key,
1189  const QString & def,
1190  bool * ok ) const
1191 {
1192  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1193 
1194  QVariant value;
1195 
1196  if ( property )
1197  {
1198  value = property->value();
1199  }
1200 
1201  bool valid = value.canConvert( QVariant::String );
1202 
1203  if ( ok )
1204  {
1205  *ok = valid;
1206  }
1207 
1208  if ( valid )
1209  {
1210  return value.toString();
1211  }
1212 
1213  return QString( def );
1214 } // QgsProject::readEntry
1215 
1216 
1217 int
1218 QgsProject::readNumEntry( QString const &scope, const QString & key, int def,
1219  bool * ok ) const
1220 {
1221  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1222 
1223  QVariant value;
1224 
1225  if ( property )
1226  {
1227  value = property->value();
1228  }
1229 
1230  bool valid = value.canConvert( QVariant::String );
1231 
1232  if ( ok )
1233  {
1234  *ok = valid;
1235  }
1236 
1237  if ( valid )
1238  {
1239  return value.toInt();
1240  }
1241 
1242  return def;
1243 } // QgsProject::readNumEntry
1244 
1245 
1246 double
1247 QgsProject::readDoubleEntry( QString const &scope, const QString & key,
1248  double def,
1249  bool * ok ) const
1250 {
1251  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1252 
1253  QVariant value;
1254 
1255  if ( property )
1256  {
1257  value = property->value();
1258  }
1259 
1260  bool valid = value.canConvert( QVariant::Double );
1261 
1262  if ( ok )
1263  {
1264  *ok = valid;
1265  }
1266 
1267  if ( valid )
1268  {
1269  return value.toDouble();
1270  }
1271 
1272  return def;
1273 } // QgsProject::readDoubleEntry
1274 
1275 
1276 bool
1277 QgsProject::readBoolEntry( QString const &scope, const QString & key, bool def,
1278  bool * ok ) const
1279 {
1280  QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1281 
1282  QVariant value;
1283 
1284  if ( property )
1285  {
1286  value = property->value();
1287  }
1288 
1289  bool valid = value.canConvert( QVariant::Bool );
1290 
1291  if ( ok )
1292  {
1293  *ok = valid;
1294  }
1295 
1296  if ( valid )
1297  {
1298  return value.toBool();
1299  }
1300 
1301  return def;
1302 } // QgsProject::readBoolEntry
1303 
1304 
1305 bool QgsProject::removeEntry( QString const &scope, const QString & key )
1306 {
1307  removeKey_( scope, key, imp_->properties_ );
1308 
1309  dirty( true );
1310 
1311  return ! findKey_( scope, key, imp_->properties_ );
1312 } // QgsProject::removeEntry
1313 
1314 
1315 
1316 QStringList QgsProject::entryList( QString const &scope, QString const &key ) const
1317 {
1318  QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1319 
1320  QStringList entries;
1321 
1322  if ( foundProperty )
1323  {
1324  QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1325 
1326  if ( propertyKey )
1327  { propertyKey->entryList( entries ); }
1328  }
1329 
1330  return entries;
1331 } // QgsProject::entryList
1332 
1333 
1334 QStringList QgsProject::subkeyList( QString const &scope, QString const &key ) const
1335 {
1336  QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1337 
1338  QStringList entries;
1339 
1340  if ( foundProperty )
1341  {
1342  QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1343 
1344  if ( propertyKey )
1345  { propertyKey->subkeyList( entries ); }
1346  }
1347 
1348  return entries;
1349 
1350 } // QgsProject::subkeyList
1351 
1352 
1353 
1355 {
1356  dump_( imp_->properties_ );
1357 } // QgsProject::dumpProperties
1358 
1359 
1360 // return the absolute path from a filename read from project file
1361 QString QgsProject::readPath( QString src ) const
1362 {
1363  if ( readBoolEntry( "Paths", "/Absolute", false ) )
1364  {
1365  return src;
1366  }
1367 
1368  // if this is a VSIFILE, remove the VSI prefix and append to final result
1369  QString vsiPrefix = qgsVsiPrefix( src );
1370  if ( ! vsiPrefix.isEmpty() )
1371  {
1372  src.remove( 0, vsiPrefix.size() );
1373  }
1374 
1375  // relative path should always start with ./ or ../
1376  if ( !src.startsWith( "./" ) && !src.startsWith( "../" ) )
1377  {
1378 #if defined(Q_OS_WIN)
1379  if ( src.startsWith( "\\\\" ) ||
1380  src.startsWith( "//" ) ||
1381  ( src[0].isLetter() && src[1] == ':' ) )
1382  {
1383  // UNC or absolute path
1384  return vsiPrefix + src;
1385  }
1386 #else
1387  if ( src[0] == '/' )
1388  {
1389  // absolute path
1390  return vsiPrefix + src;
1391  }
1392 #endif
1393 
1394  // so this one isn't absolute, but also doesn't start // with ./ or ../.
1395  // That means that it was saved with an earlier version of "relative path support",
1396  // where the source file had to exist and only the project directory was stripped
1397  // from the filename.
1398  QString home = homePath();
1399  if ( home.isNull() )
1400  return vsiPrefix + src;
1401 
1402  QFileInfo fi( home + "/" + src );
1403 
1404  if ( !fi.exists() )
1405  {
1406  return vsiPrefix + src;
1407  }
1408  else
1409  {
1410  return vsiPrefix + fi.canonicalFilePath();
1411  }
1412  }
1413 
1414  QString srcPath = src;
1415  QString projPath = fileName();
1416 
1417  if ( projPath.isEmpty() )
1418  {
1419  return vsiPrefix + src;
1420  }
1421 
1422 #if defined(Q_OS_WIN)
1423  srcPath.replace( "\\", "/" );
1424  projPath.replace( "\\", "/" );
1425 
1426  bool uncPath = projPath.startsWith( "//" );
1427 #endif
1428 
1429  QStringList srcElems = srcPath.split( "/", QString::SkipEmptyParts );
1430  QStringList projElems = projPath.split( "/", QString::SkipEmptyParts );
1431 
1432 #if defined(Q_OS_WIN)
1433  if ( uncPath )
1434  {
1435  projElems.insert( 0, "" );
1436  projElems.insert( 0, "" );
1437  }
1438 #endif
1439 
1440  // remove project file element
1441  projElems.removeLast();
1442 
1443  // append source path elements
1444  projElems << srcElems;
1445  projElems.removeAll( "." );
1446 
1447  // resolve ..
1448  int pos;
1449  while (( pos = projElems.indexOf( ".." ) ) > 0 )
1450  {
1451  // remove preceding element and ..
1452  projElems.removeAt( pos - 1 );
1453  projElems.removeAt( pos - 1 );
1454  }
1455 
1456 #if !defined(Q_OS_WIN)
1457  // make path absolute
1458  projElems.prepend( "" );
1459 #endif
1460 
1461  return vsiPrefix + projElems.join( "/" );
1462 }
1463 
1464 // return the absolute or relative path to write it to the project file
1465 QString QgsProject::writePath( QString src ) const
1466 {
1467  if ( readBoolEntry( "Paths", "/Absolute", false ) || src.isEmpty() )
1468  {
1469  return src;
1470  }
1471 
1472  QString srcPath = src;
1473  QString projPath = fileName();
1474 
1475  if ( projPath.isEmpty() )
1476  {
1477  return src;
1478  }
1479 
1480  // if this is a VSIFILE, remove the VSI prefix and append to final result
1481  QString vsiPrefix = qgsVsiPrefix( src );
1482  if ( ! vsiPrefix.isEmpty() )
1483  {
1484  srcPath.remove( 0, vsiPrefix.size() );
1485  }
1486 
1487 #if defined( Q_OS_WIN )
1488  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1489 
1490  srcPath.replace( "\\", "/" );
1491 
1492  if ( srcPath.startsWith( "//" ) )
1493  {
1494  // keep UNC prefix
1495  srcPath = "\\\\" + srcPath.mid( 2 );
1496  }
1497 
1498  projPath.replace( "\\", "/" );
1499  if ( projPath.startsWith( "//" ) )
1500  {
1501  // keep UNC prefix
1502  projPath = "\\\\" + projPath.mid( 2 );
1503  }
1504 #else
1505  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1506 #endif
1507 
1508  QStringList projElems = projPath.split( "/", QString::SkipEmptyParts );
1509  QStringList srcElems = srcPath.split( "/", QString::SkipEmptyParts );
1510 
1511  // remove project file element
1512  projElems.removeLast();
1513 
1514  projElems.removeAll( "." );
1515  srcElems.removeAll( "." );
1516 
1517  // remove common part
1518  int n = 0;
1519  while ( srcElems.size() > 0 &&
1520  projElems.size() > 0 &&
1521  srcElems[0].compare( projElems[0], cs ) == 0 )
1522  {
1523  srcElems.removeFirst();
1524  projElems.removeFirst();
1525  n++;
1526  }
1527 
1528  if ( n == 0 )
1529  {
1530  // no common parts; might not even by a file
1531  return src;
1532  }
1533 
1534  if ( projElems.size() > 0 )
1535  {
1536  // go up to the common directory
1537  for ( int i = 0; i < projElems.size(); i++ )
1538  {
1539  srcElems.insert( 0, ".." );
1540  }
1541  }
1542  else
1543  {
1544  // let it start with . nevertheless,
1545  // so relative path always start with either ./ or ../
1546  srcElems.insert( 0, "." );
1547  }
1548 
1549  return vsiPrefix + srcElems.join( "/" );
1550 }
1551 
1552 void QgsProject::setError( QString errorMessage )
1553 {
1554  mErrorMessage = errorMessage;
1555 }
1556 
1557 QString QgsProject::error() const
1558 {
1559  return mErrorMessage;
1560 }
1561 
1563 {
1564  setError( QString() );
1565 }
1566 
1568 {
1569  delete mBadLayerHandler;
1570  mBadLayerHandler = handler;
1571 }
1572 
1573 QString QgsProject::layerIsEmbedded( const QString& id ) const
1574 {
1575  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1576  if ( it == mEmbeddedLayers.constEnd() )
1577  {
1578  return QString();
1579  }
1580  return it.value().first;
1581 }
1582 
1583 bool QgsProject::createEmbeddedLayer( const QString& layerId, const QString& projectFilePath, QList<QDomNode>& brokenNodes,
1584  QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList, bool saveFlag )
1585 {
1586  QFile projectFile( projectFilePath );
1587  if ( !projectFile.open( QIODevice::ReadOnly ) )
1588  {
1589  return false;
1590  }
1591 
1592  QDomDocument projectDocument;
1593  if ( !projectDocument.setContent( &projectFile ) )
1594  {
1595  return false;
1596  }
1597 
1598  //does project store pathes absolute or relative?
1599  bool useAbsolutePathes = true;
1600  QDomElement propertiesElem = projectDocument.documentElement().firstChildElement( "properties" );
1601  if ( !propertiesElem.isNull() )
1602  {
1603  QDomElement absElem = propertiesElem.firstChildElement( "Paths" ).firstChildElement( "Absolute" );
1604  if ( !absElem.isNull() )
1605  {
1606  useAbsolutePathes = absElem.text().compare( "true", Qt::CaseInsensitive ) == 0;
1607  }
1608  }
1609 
1610  QDomElement projectLayersElem = projectDocument.documentElement().firstChildElement( "projectlayers" );
1611  if ( projectLayersElem.isNull() )
1612  {
1613  return false;
1614  }
1615 
1616  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( "maplayer" );
1617  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1618  {
1619  //get layer id
1620  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1621  QString id = mapLayerElem.firstChildElement( "id" ).text();
1622  if ( id == layerId )
1623  {
1624  //layer can be embedded only once
1625  if ( mapLayerElem.attribute( "embedded" ) == "1" )
1626  {
1627  return false;
1628  }
1629 
1630  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1631 
1632  //change datasource path from relative to absolute if necessary
1633  if ( !useAbsolutePathes )
1634  {
1635  QDomElement provider = mapLayerElem.firstChildElement( "provider" );
1636  if ( provider.text() == "spatialite" )
1637  {
1638  QDomElement dsElem = mapLayerElem.firstChildElement( "datasource" );
1639 
1640  QgsDataSourceURI uri( dsElem.text() );
1641 
1642  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + "/" + uri.database() );
1643  if ( absoluteDs.exists() )
1644  {
1645  uri.setDatabase( absoluteDs.absoluteFilePath() );
1646  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1647  dsElem.appendChild( projectDocument.createTextNode( uri.uri() ) );
1648  }
1649  }
1650  else
1651  {
1652  QDomElement dsElem = mapLayerElem.firstChildElement( "datasource" );
1653  QString debug( QFileInfo( projectFilePath ).absolutePath() + "/" + dsElem.text() );
1654  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + "/" + dsElem.text() );
1655  if ( absoluteDs.exists() )
1656  {
1657  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1658  dsElem.appendChild( projectDocument.createTextNode( absoluteDs.absoluteFilePath() ) );
1659  }
1660  }
1661  }
1662 
1663  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1664  {
1665  return true;
1666  }
1667  else
1668  {
1669  mEmbeddedLayers.remove( layerId );
1670  return false;
1671  }
1672  }
1673  }
1674 
1675  return false;
1676 }
1677 
1678 void QgsProject::setSnapSettingsForLayer( const QString& layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
1679 {
1680  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1681  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1682  int idx = layerIdList.indexOf( layerId );
1683  if ( idx != -1 )
1684  {
1685  layerIdList.removeAt( idx );
1686  enabledList.removeAt( idx );
1687  snapTypeList.removeAt( idx );
1688  toleranceUnitList.removeAt( idx );
1689  toleranceList.removeAt( idx );
1690  avoidIntersectionList.removeOne( layerId );
1691  }
1692 
1693  layerIdList.append( layerId );
1694 
1695  //enabled
1696  enabledList.append( enabled ? "enabled" : "disabled" );
1697 
1698  //snap type
1699  QString typeString;
1700  if ( type == QgsSnapper::SnapToSegment )
1701  {
1702  typeString = "to_segment";
1703  }
1704  else if ( type == QgsSnapper::SnapToVertexAndSegment )
1705  {
1706  typeString = "to_vertex_and_segment";
1707  }
1708  else
1709  {
1710  typeString = "to_vertex";
1711  }
1712  snapTypeList.append( typeString );
1713 
1714  //units
1715  toleranceUnitList.append( unit == QgsTolerance::Pixels ? "1" : "0" );
1716 
1717  //tolerance
1718  toleranceList.append( QString::number( tolerance ) );
1719 
1720  //avoid intersection
1721  if ( avoidIntersection )
1722  {
1723  avoidIntersectionList.append( layerId );
1724  }
1725 
1726  writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
1727  writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
1728  writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
1729  writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
1730  writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
1731  writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
1732  emit snapSettingsChanged();
1733 }
1734 
1735 bool QgsProject::snapSettingsForLayer( const QString& layerId, bool& enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType& units, double& tolerance,
1736  bool& avoidIntersection ) const
1737 {
1738  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1739  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1740  int idx = layerIdList.indexOf( layerId );
1741  if ( idx == -1 )
1742  {
1743  return false;
1744  }
1745 
1746  //make sure all lists are long enough
1747  int minListEntries = idx + 1;
1748  if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
1749  toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
1750  {
1751  return false;
1752  }
1753 
1754  //enabled
1755  enabled = enabledList.at( idx ) == "enabled";
1756 
1757  //snap type
1758  QString snapType = snapTypeList.at( idx );
1759  if ( snapType == "to_segment" )
1760  {
1762  }
1763  else if ( snapType == "to_vertex_and_segment" )
1764  {
1766  }
1767  else //to vertex
1768  {
1769  type = QgsSnapper::SnapToVertex;
1770  }
1771 
1772  //units
1773  if ( toleranceUnitList.at( idx ) == "1" )
1774  {
1775  units = QgsTolerance::Pixels;
1776  }
1777  else
1778  {
1779  units = QgsTolerance::MapUnits;
1780  }
1781 
1782  //tolerance
1783  tolerance = toleranceList.at( idx ).toDouble();
1784 
1785  //avoid intersection
1786  avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
1787 
1788  return true;
1789 }
1790 
1791 void QgsProject::snapSettings( QStringList& layerIdList, QStringList& enabledList, QStringList& snapTypeList, QStringList& toleranceUnitList, QStringList& toleranceList,
1792  QStringList& avoidIntersectionList ) const
1793 {
1794  layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
1795  enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
1796  toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
1797  toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
1798  snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
1799  avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
1800 }
1801 
1803 {
1804  QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
1805  emit snapSettingsChanged();
1806 }
1807 
1809 {
1810  return ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) > 0 );
1811 }
1812 
1813 void QgsProjectBadLayerDefaultHandler::handleBadLayers( QList<QDomNode> /*layers*/, QDomDocument /*projectDom*/ )
1814 {
1815  // just ignore any bad layers
1816 }
1817 
1818 QString QgsProject::homePath() const
1819 {
1820  QFileInfo pfi( fileName() );
1821  if ( !pfi.exists() )
1822  return QString::null;
1823 
1824  return pfi.canonicalPath();
1825 }
1826 
1828 {
1829  return mRelationManager;
1830 }
QString mErrorMessage
Definition: qgsproject.h:392
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:83
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:47
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:399
QgsPropertyKey properties_
property hierarchy
Definition: qgsproject.cpp:298
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:157
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
QString qgsVsiPrefix(QString path)
Definition: qgis.cpp:242
bool dirty
true if project has been modified since it has been read or saved
Definition: qgsproject.cpp:304
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:317
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:66
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
void dump(int tabs=0) const
dumps out the keys and values
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:790
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:92
bool removeEntry(const QString &scope, const QString &key)
remove the given key
Reads and writes project states.
Definition: qgsproject.h:65
bool readXML(QDomNode &keyNode)
restores property hierarchy to given Dom node
QFile file
current physical project file
Definition: qgsproject.cpp:295
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:394
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:76
bool addLayer(const QDomElement &layerElem, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList)
Definition: qgsproject.cpp:720
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.
This class manages a set of relations between layers.
static QgsProject * theProject_
Definition: qgsproject.h:388
std::auto_ptr< Imp > imp_
implementation handle
Definition: qgsproject.h:383
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:362
static void removeKey_(QString const &scope, QString const &key, QgsPropertyKey &rootProperty)
Definition: qgsproject.cpp:230
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:301
const QString & title() const
returns title
Definition: qgsproject.cpp:379
QgsRelationManager * mRelationManager
Definition: qgsproject.h:404
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:55
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:421
virtual bool isKey() const =0
returns true if is a QgsPropertyKey
Represents a vector layer which manages a vector based data sets.
QgsRelationManager * relationManager() const
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:411
void snapSettingsChanged()
virtual void handleBadLayers(QList< QDomNode > layers, QDomDocument projectDom)=0
void dirty(bool b)
Definition: qgsproject.cpp:391
#define tr(sourceText)