QGIS/src/core/qgsproject.cpp
2008-10-24 21:47:48 +00:00

1301 lines
33 KiB
C++

/***************************************************************************
qgsproject.cpp - description
-------------------
begin : July 23, 2004
copyright : (C) 2004 by Mark Coletti
email : mcoletti at gmail.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsproject.h"
#include <deque>
#include <memory>
#include <cassert>
#include <iostream>
#include "qgslogger.h"
#include "qgsrect.h"
#include "qgsvectorlayer.h"
#include "qgsrasterlayer.h"
#include "qgsmaplayerregistry.h"
#include "qgsexception.h"
#include "qgsprojectproperty.h"
#include "qgslogger.h"
#include "qgsprojectfiletransform.h"
#include "qgsprojectversion.h"
#include <QApplication>
#include <QFileInfo>
#include <QDomNode>
#include <QObject>
#include <QTextStream>
static const char *const ident_ = "$Id$";
// / canonical project instance
QgsProject * QgsProject::theProject_;
/**
Take the given scope and key and convert them to a string list of key
tokens that will be used to navigate through a Property hierarchy
E.g., scope "someplugin" and key "/foo/bar/baz" will become a string list
of { "properties", "someplugin", "foo", "bar", "baz" }. "properties" is
always first because that's the permanent ``root'' Property node.
*/
static
QStringList makeKeyTokens_( QString const &scope, QString const &key )
{
// XXX - debugger probes
//const char * scope_str = scope.toLocal8Bit().data();
//const char * key_str = key.toLocal8Bit().data();
QStringList keyTokens = QStringList( scope );
keyTokens += key.split( '/', QString::SkipEmptyParts );
// be sure to include the canonical root node
keyTokens.push_front( "properties" );
return keyTokens;
} // makeKeyTokens_
/**
return the property that matches the given key sequence, if any
@param sequence is a tokenized key sequence
@param p is likely to be the top level QgsPropertyKey in QgsProject:e:Imp.
@return null if not found, otherwise located Property
*/
static
QgsProperty * findKey_( QString const & scope,
QString const & key,
QgsPropertyKey & rootProperty )
{
QgsPropertyKey * currentProperty = &rootProperty;
QgsProperty * nextProperty; // link to next property down hiearchy
QStringList keySequence = makeKeyTokens_( scope, key );
while ( ! keySequence.isEmpty() )
{
// if the current head of the sequence list matches the property name,
// then traverse down the property hierarchy
if ( keySequence.first() == currentProperty->name() )
{
// remove front key since we're traversing down a level
keySequence.pop_front();
// if we have only one key name left, then return the key found
if ( 1 == keySequence.count() )
{
return currentProperty->find( keySequence.front() );
}
// if we're out of keys then the current property is the one we
// want; i.e., we're in the rate case of being at the top-most
// property node
else if ( keySequence.isEmpty() )
{
return currentProperty;
}
else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
{
if ( nextProperty->isKey() )
{
currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
}
// it may be that this may be one of several property value
// nodes keyed by QDict string; if this is the last remaining
// key token and the next property is a value node, then
// that's the situation, so return the currentProperty
else if ( nextProperty->isValue() && ( 1 == keySequence.count() ) )
{
return currentProperty;
}
else // QgsPropertyValue not Key, so return null
{
return 0x0;
}
}
else // if the next key down isn't found
{ // then the overall key sequence doesn't exist
return 0x0;
}
}
else
{
return 0x0;
}
}
return 0x0;
} // findKey_
/** add the given key and value
@param sequence is list of keys
@param rootProperty is the property from which to start adding
@param value the value associated with the key
*/
static
QgsProperty * addKey_( QString const & scope,
QString const & key,
QgsPropertyKey * rootProperty,
QVariant value )
{
QStringList keySequence = makeKeyTokens_( scope, key );
// cursor through property key/value hierarchy
QgsPropertyKey * currentProperty = rootProperty;
QgsProperty * newProperty; // link to next property down hiearchy
while ( ! keySequence.isEmpty() )
{
// if the current head of the sequence list matches the property name,
// then traverse down the property hierarchy
if ( keySequence.first() == currentProperty->name() )
{
// remove front key since we're traversing down a level
keySequence.pop_front();
// if key sequence has one last element, then we use that as the
// name to store the value
if ( 1 == keySequence.count() )
{
currentProperty->setValue( keySequence.front(), value );
return currentProperty;
}
// we're at the top element if popping the keySequence element
// will leave it empty; in that case, just add the key
else if ( keySequence.isEmpty() )
{
currentProperty->setValue( value );
return currentProperty;
}
else if (( newProperty = currentProperty->find( keySequence.first() ) ) )
{
currentProperty = dynamic_cast<QgsPropertyKey*>( newProperty );
if ( currentProperty )
{
continue;
}
else // QgsPropertyValue not Key, so return null
{
return 0x0;
}
}
else // the next subkey doesn't exist, so add it
{
newProperty = currentProperty->addKey( keySequence.first() );
if ( newProperty )
{
currentProperty = dynamic_cast<QgsPropertyKey*>( newProperty );
}
continue;
}
}
else
{
return 0x0;
}
}
return 0x0;
} // addKey_
static
void removeKey_( QString const & scope,
QString const & key,
QgsPropertyKey & rootProperty )
{
QgsPropertyKey * currentProperty = &rootProperty;
QgsProperty * nextProperty = NULL; // link to next property down hiearchy
QgsPropertyKey * previousQgsPropertyKey = NULL; // link to previous property up hiearchy
QStringList keySequence = makeKeyTokens_( scope, key );
while ( ! keySequence.isEmpty() )
{
// if the current head of the sequence list matches the property name,
// then traverse down the property hierarchy
if ( keySequence.first() == currentProperty->name() )
{
// remove front key since we're traversing down a level
keySequence.pop_front();
// if we have only one key name left, then try to remove the key
// with that name
if ( 1 == keySequence.count() )
{
currentProperty->removeKey( keySequence.front() );
}
// if we're out of keys then the current property is the one we
// want to remove, but we can't delete it directly; we need to
// delete it from the parent property key container
else if ( keySequence.isEmpty() )
{
previousQgsPropertyKey->removeKey( currentProperty->name() );
}
else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
{
previousQgsPropertyKey = currentProperty;
currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
if ( currentProperty )
{
continue;
}
else // QgsPropertyValue not Key, so return null
{
return;
}
}
else // if the next key down isn't found
{ // then the overall key sequence doesn't exist
return;
}
}
else
{
return;
}
}
} // void removeKey_
struct QgsProject::Imp
{
/// current physical project file
QFile file;
/// property hierarchy
QgsPropertyKey properties_;
/// project title
QString title;
/// true if project has been modified since it has been read or saved
bool dirty;
Imp()
: title( "" ),
dirty( false )
{ // top property node is the root
// "properties" that contains all plug-in
// and extra property keys and values
properties_.name() = "properties"; // root property node always this value
}
/** clear project properties when a new project is started
*/
void clear()
{
//QgsDebugMsg( "Clearing project properties Impl->clear();" );
properties_.clearKeys();
title = "";
// reset some default project properties
// XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
QgsProject::instance()->writeEntry( "PositionPrecision", "/Automatic", true );
QgsProject::instance()->writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
}
}; // struct QgsProject::Imp
QgsProject::QgsProject()
: imp_( new QgsProject::Imp )
{
// Set some default project properties
// XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
writeEntry( "PositionPrecision", "/Automatic", true );
writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
// XXX writeEntry() makes the project dirty, but it doesn't make sense
// for a new project to be dirty, so let's clean it up
dirty( false );
} // QgsProject ctor
QgsProject::~QgsProject()
{
// note that std::auto_ptr automatically deletes imp_ when it's destroyed
} // QgsProject dtor
QgsProject * QgsProject::instance()
{
if ( !QgsProject::theProject_ )
{
QgsProject::theProject_ = new QgsProject;
}
return QgsProject::theProject_;
} // QgsProject * instance()
void QgsProject::title( QString const &title )
{
imp_->title = title;
dirty( true );
} // void QgsProject::title
QString const & QgsProject::title() const
{
return imp_->title;
} // QgsProject::title() const
bool QgsProject::isDirty() const
{
return imp_->dirty;
} // bool QgsProject::isDirty()
void QgsProject::dirty( bool b )
{
imp_->dirty = b;
} // bool QgsProject::isDirty()
void QgsProject::setFileName( QString const &name )
{
imp_->file.setFileName( name );
dirty( true );
} // void QgsProject::setFileName( QString const & name )
QString QgsProject::fileName() const
{
return imp_->file.fileName();
} // QString QgsProject::fileName() const
/// basically a debugging tool to dump property list values
static void dump_( QgsPropertyKey const & topQgsPropertyKey )
{
qDebug( "current properties:" );
topQgsPropertyKey.dump();
} // dump_
/**
Restore any optional properties found in "doc" to "properties".
<properties> tags for all optional properties. Within that there will be
scope tags. In the following example there exist one property in the
"fsplugin" scope. "layers" is a list containing three string values.
<properties>
<fsplugin>
<foo type="int" >42</foo>
<baz type="int" >1</baz>
<layers type="QStringList" >
<value>railroad</value>
<value>airport</value>
</layers>
<xyqzzy type="int" >1</xyqzzy>
<bar type="double" >123.456</bar>
<feature_types type="QStringList" >
<value>type</value>
</feature_types>
</fsplugin>
</properties>
@param project_properties should be the top QgsPropertyKey node.
*/
static
void
_getProperties( QDomDocument const &doc, QgsPropertyKey & project_properties )
{
QDomNodeList properties = doc.elementsByTagName( "properties" );
if ( properties.count() > 1 )
{
qDebug( "there appears to be more than one ``properties'' XML tag ... bailing" );
return;
}
else if ( properties.count() < 1 ) // no properties found, so we're done
{
return;
}
// item(0) because there should only be ONE
// "properties" node
QDomNodeList scopes = properties.item( 0 ).childNodes();
if ( scopes.count() < 1 )
{
qDebug( "empty ``properties'' XML tag ... bailing" );
return;
}
QDomNode propertyNode = properties.item( 0 );
if ( ! project_properties.readXML( propertyNode ) )
{
qDebug( "Project_properties.readXML() failed" );
}
// DEPRECATED as functionality has been shoved down to QgsProperyKey::readXML()
// size_t i = 0;
// while (i < scopes.count())
// {
// QDomNode curr_scope_node = scopes.item(i);
// qDebug("found %d property node(s) for scope %s",
// curr_scope_node.childNodes().count(),
// curr_scope_node.nodeName().utf8().constData());
// QString key(curr_scope_node.nodeName());
// QgsPropertyKey * currentKey =
// dynamic_cast<QgsPropertyKey*>(project_properties.find( key ));
// if ( ! currentKey )
// {
// // if the property key doesn't yet exist, create an empty instance
// // of that key
// currentKey = project_properties.addKey( key );
// if ( ! currentKey )
// {
// qDebug( "%s:%d unable to add key", __FILE__, __LINE__ );
// }
// }
// if (! currentKey->readXML(curr_scope_node))
// {
// qDebug("%s:%d unable to read XML for property %s", __FILE__, __LINE__,
// curr_scope_node.nodeName().utf8().constData());
// }
// ++i;
// }
} // _getProperties
/**
Get the project title
XML in file has this form:
<qgis projectname="default project">
<title>a project title</title>
@todo XXX we should go with the attribute xor <title>, not both.
*/
static void _getTitle( QDomDocument const &doc, QString & title )
{
QDomNodeList nl = doc.elementsByTagName( "title" );
title = ""; // by default the title will be empty
if ( !nl.count() )
{
qDebug( "%s : %d %s", __FILE__, __LINE__, " unable to find title element\n" );
return;
}
QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element ok
if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
{
qDebug( "%s : %d %s", __FILE__, __LINE__, " unable to find title element\n" );
return;
}
QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
if ( !titleTextNode.isText() )
{
qDebug( "%s : %d %s", __FILE__, __LINE__, " unable to find title element\n" );
return;
}
QDomText titleText = titleTextNode.toText();
title = titleText.data();
} // _getTitle
/** return the version string found in the given Dom document
@returns the version string or an empty string if none found
*/
static QgsProjectVersion _getVersion( QDomDocument const &doc )
{
QDomNodeList nl = doc.elementsByTagName( "qgis" );
if ( !nl.count() )
{
QgsDebugMsg( " unable to find qgis element in project file" );
return QgsProjectVersion( 0, 0, 0, QString( "" ) );
}
QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
QgsProjectVersion projectVersion( qgisElement.attribute( "version" ) );
return projectVersion;
} // _getVersion
/**
Read map layers from project file
@returns pair of < bool, list<QDomNode> >; bool is true if function worked;
else is false. list contains nodes corresponding to layers that couldn't
be loaded
@note XML of form:
<maplayer type="vector">
<layername>Hydrop</layername>
<datasource>/data/usgs/city_shp/hydrop.shp</datasource>
<zorder>0</zorder>
<provider>ogr</provider>
<singlesymbol>
<renderitem>
<value>blabla</value>
<symbol>
<outlinecolor red="85" green="0" blue="255" />
<outlinestyle>SolidLine</outlinestyle>
<outlinewidth>1</outlinewidth>
<fillcolor red="0" green="170" blue="255" />
<fillpattern>SolidPattern</fillpattern>
</symbol>
<label>blabla</label>
</renderitem>
</singlesymbol>
<label>0</label>
<labelattributes>
<label text="Label" field="" />
<family name="Sans Serif" field="" />
<size value="12" units="pt" field="" />
<bold on="0" field="" />
<italic on="0" field="" />
<underline on="0" field="" />
<color red="0" green="0" blue="0" field="" />
<x field="" />
<y field="" />
<offset units="pt" x="0" xfield="" y="0" yfield="" />
<angle value="0" field="" />
<alignment value="center" field="" />
</labelattributes>
</maplayer>
*/
static std::pair< bool, std::list<QDomNode> > _getMapLayers( QDomDocument const &doc )
{
// Layer order is implicit in the order they are stored in the project file
QDomNodeList nl = doc.elementsByTagName( "maplayer" );
// XXX what is this used for? QString layerCount( QString::number(nl.count()) );
QString wk;
std::list<QDomNode> brokenNodes; // a list of Dom nodes corresponding to layers
// that we were unable to load; this could be
// because the layers were removed or
// re-located after the project was last saved
// process the map layer nodes
if ( 0 == nl.count() ) // if we have no layers to process, bail
{
return make_pair( true, brokenNodes ); // Decided to return "true" since it's
// possible for there to be a project with no
// layers; but also, more imporantly, this
// would cause the tests/qgsproject to fail
// since the test suite doesn't currently
// support test layers
}
bool returnStatus = true;
for ( int i = 0; i < nl.count(); i++ )
{
QDomNode node = nl.item( i );
QDomElement element = node.toElement();
QString type = element.attribute( "type" );
QgsDebugMsg( "Layer type is " + type );
QgsMapLayer *mapLayer = NULL;
if ( type == "vector" )
{
mapLayer = new QgsVectorLayer;
}
else if ( type == "raster" )
{
mapLayer = new QgsRasterLayer;
}
Q_CHECK_PTR( mapLayer );
if ( !mapLayer )
{
QgsDebugMsg( "Unable to create layer" );
return make_pair( false, brokenNodes );
}
// have the layer restore state that is stored in Dom node
if ( mapLayer->readXML( node ) )
{
mapLayer = QgsMapLayerRegistry::instance()->addMapLayer( mapLayer );
}
else
{
delete mapLayer;
QgsDebugMsg( "Unable to load " + type + " layer" );
returnStatus = false; // flag that we had problems loading layers
brokenNodes.push_back( node );
}
}
return make_pair( returnStatus, brokenNodes );
} // _getMapLayers
/**
Please note that most of the contents were copied from qgsproject
*/
bool QgsProject::read( QFileInfo const &file )
{
imp_->file.setFileName( file.filePath() );
return read();
} // QgsProject::read( QFile & file )
/**
@note it's presumed that the caller has already reset the map canvas, map
registry, and legend
*/
bool QgsProject::read()
{
std::auto_ptr< QDomDocument > doc =
std::auto_ptr < QDomDocument > ( new QDomDocument( "qgis" ) );
if ( !imp_->file.open( QIODevice::ReadOnly ) )
{
imp_->file.close(); // even though we got an error, let's make
// sure it's closed anyway
throw QgsIOException( QObject::tr( "Unable to open " ) + imp_->file.fileName() );
}
// location of problem associated with errorMsg
int line, column;
QString errorMsg;
if ( !doc->setContent( &imp_->file, &errorMsg, &line, &column ) )
{
// want to make this class as GUI independent as possible; so commented out
// QMessageBox::critical( 0x0, "Project File Read Error",
// errorMsg + " at line " + QString::number( line ) +
// " column " + QString::number( column ) );
QString errorString = QObject::tr( "Project file read error: " ) +
errorMsg + QObject::tr( " at line " ) + QString::number( line ) + QObject::tr( " column " ) +
QString::number( column );
qDebug( errorString.toUtf8().constData() );
imp_->file.close();
throw QgsException( errorString + QObject::tr( " for file " ) + imp_->file.fileName() );
}
imp_->file.close();
QgsDebugMsg( "Opened document " + imp_->file.fileName() );
QgsDebugMsg( "Project title: " + imp_->title );
// get project version string, if any
QgsProjectVersion fileVersion = _getVersion( *doc );
QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
if ( thisVersion > fileVersion )
{
QgsLogger::warning( "Loading a file that was saved with an older "
"version of qgis (saved in " + fileVersion.text() +
", loaded in " + QGis::QGIS_VERSION +
"). Problems may occur." );
QgsProjectFileTransform projectFile( *doc, fileVersion );
//! Shows a warning when an old project file is read.
emit oldProjectVersionWarning( fileVersion.text() );
QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
projectFile.dump();
projectFile.updateRevision( thisVersion );
projectFile.dump();
}
// before we start loading everything, let's clear out the current set of
// properties first so that we don't have the properties from the previous
// project still hanging around
imp_->clear();
// now get any properties
_getProperties( *doc, imp_->properties_ );
QgsDebugMsg( QString::number( imp_->properties_.count() ) + " properties read" );
dump_( imp_->properties_ );
// restore the canvas' area of interest
// now get project title
_getTitle( *doc, imp_->title );
// get the map layers
std::pair< bool, std::list<QDomNode> > getMapLayersResults = _getMapLayers( *doc );
// review the integrity of the retrieved map layers
if ( ! getMapLayersResults.first )
{
QgsDebugMsg( "Unable to get map layers from project file." );
if ( ! getMapLayersResults.second.empty() )
{
QgsDebugMsg( "there are " + QString::number( getMapLayersResults.second.size() ) + " broken layers" );
}
// Since we could be executing this from the test harness which
// doesn't *have* layers -- nor a GUI for that matter -- we'll just
// leave in the whining and boldly stomp on.
emit readProject( *doc );
throw QgsProjectBadLayerException( getMapLayersResults.second );
// return false;
}
// read the project: used by map canvas and legend
emit readProject( *doc );
// can't be dirty since we're allegedly in pristine state
dirty( false );
return true;
} // QgsProject::read
bool QgsProject::read( QDomNode & layerNode )
{
QString type = layerNode.toElement().attribute( "type" );
QgsMapLayer *mapLayer;
if ( type == "vector" )
{
mapLayer = new QgsVectorLayer;
}
else if ( type == "raster" )
{
mapLayer = new QgsRasterLayer;
}
else
{
QgsDebugMsg( "bad layer type" );
return false;
}
if ( !mapLayer )
{
QgsDebugMsg( "unable to create layer" );
return false;
}
// have the layer restore state that is stored in Dom node
if ( mapLayer->readXML( layerNode ) )
{
mapLayer = QgsMapLayerRegistry::instance()->addMapLayer( mapLayer );
}
else
{
delete mapLayer;
QgsDebugMsg( "unable to load " + type + " layer" );
return false;
}
return true;
} // QgsProject::read( QDomNode & layerNode )
bool QgsProject::write( QFileInfo const &file )
{
imp_->file.setFileName( file.filePath() );
return write();
} // QgsProject::write( QFileInfo const & file )
bool QgsProject::write()
{
// if we have problems creating or otherwise writing to the project file,
// let's find out up front before we go through all the hand-waving
// necessary to create all the Dom objects
if ( !imp_->file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
{
imp_->file.close(); // even though we got an error, let's make
// sure it's closed anyway
throw QgsIOException( QObject::tr( "Unable to save to file " ) + imp_->file.fileName() );
}
QFileInfo myFileInfo( imp_->file );
if ( !myFileInfo.isWritable() )
{
// even though we got an error, let's make
// sure it's closed anyway
imp_->file.close();
throw QgsIOException( imp_->file.fileName() + QObject::tr( " is not writeable." )
+ QObject::tr( "Please adjust permissions (if possible) and try again." ) );
}
QDomImplementation DomImplementation;
QDomDocumentType documentType =
DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd",
"SYSTEM" );
std::auto_ptr < QDomDocument > doc =
std::auto_ptr < QDomDocument > ( new QDomDocument( documentType ) );
QDomElement qgisNode = doc->createElement( "qgis" );
qgisNode.setAttribute( "projectname", title() );
qgisNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
doc->appendChild( qgisNode );
// title
QDomElement titleNode = doc->createElement( "title" );
qgisNode.appendChild( titleNode );
QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
titleNode.appendChild( titleText );
// let map canvas and legend write their information
emit writeProject( *doc );
// within top level node save list of layers
QMap<QString, QgsMapLayer*> & layers = QgsMapLayerRegistry::instance()->mapLayers();
// Iterate over layers in zOrder
// Call writeXML() on each
QDomElement projectLayersNode = doc->createElement( "projectlayers" );
projectLayersNode.setAttribute( "layercount", qulonglong( layers.size() ) );
QMap<QString, QgsMapLayer*>::iterator li = layers.begin();
while ( li != layers.end() )
{
//QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer(*li);
QgsMapLayer* ml = li.value();
if ( ml )
{
ml->writeXML( projectLayersNode, *doc );
}
li++;
}
qgisNode.appendChild( projectLayersNode );
// now add the optional extra properties
dump_( imp_->properties_ );
qDebug( "there are %d property scopes", static_cast<int>( imp_->properties_.count() ) );
if ( !imp_->properties_.isEmpty() ) // only worry about properties if we
// actually have any properties
{
imp_->properties_.writeXML( "properties", qgisNode, *doc );
}
// now wrap it up and ship it to the project file
doc->normalize(); // XXX I'm not entirely sure what this does
//QString xml = doc->toString(4); // write to string with indentation of four characters
// (yes, four is arbitrary)
// const char * xmlString = xml; // debugger probe point
// qDebug( "project file output:\n\n" + xml );
QTextStream projectFileStream( &imp_->file );
//projectFileStream << xml << endl;
doc->save( projectFileStream, 4 ); // save as utf-8
imp_->file.close();
// check if the text stream had no error - if it does
// the user will get a message so they can try to resolve the
// situation e.g. by saving project to a volume with more space
//
if ( projectFileStream.pos() == -1 || imp_->file.error() != QFile::NoError )
{
throw QgsIOException( QObject::tr( "Unable to save to file. Your project "
"may be corrupted on disk. Try clearing some space on the volume and "
"check file permissions before pressing save again." ) +
imp_->file.fileName() );
}
dirty( false ); // reset to pristine state
return true;
} // QgsProject::write
void QgsProject::clearProperties()
{
//QgsDebugMsg("entered.");
imp_->clear();
dirty( true );
} // QgsProject::clearProperties()
bool
QgsProject::writeEntry( QString const &scope, const QString & key, bool value )
{
dirty( true );
return addKey_( scope, key, &imp_->properties_, value );
} // QgsProject::writeEntry ( ..., bool value )
bool
QgsProject::writeEntry( QString const &scope, const QString & key,
double value )
{
dirty( true );
return addKey_( scope, key, &imp_->properties_, value );
} // QgsProject::writeEntry ( ..., double value )
bool
QgsProject::writeEntry( QString const &scope, const QString & key, int value )
{
dirty( true );
return addKey_( scope, key, &imp_->properties_, value );
} // QgsProject::writeEntry ( ..., int value )
bool
QgsProject::writeEntry( QString const &scope, const QString & key,
const QString & value )
{
dirty( true );
return addKey_( scope, key, &imp_->properties_, value );
} // QgsProject::writeEntry ( ..., const QString & value )
bool
QgsProject::writeEntry( QString const &scope, const QString & key,
const QStringList & value )
{
dirty( true );
return addKey_( scope, key, &imp_->properties_, value );
} // QgsProject::writeEntry ( ..., const QStringList & value )
QStringList
QgsProject::readListEntry( QString const & scope,
const QString & key,
bool * ok ) const
{
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
QVariant value;
if ( property )
{
value = property->value();
}
bool valid = QVariant::StringList == value.type();
if ( ok )
{
*ok = valid;
}
if ( valid )
{
return value.toStringList();
}
return QStringList();
} // QgsProject::readListEntry
QString
QgsProject::readEntry( QString const & scope,
const QString & key,
const QString & def,
bool * ok ) const
{
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
QVariant value;
if ( property )
{
value = property->value();
}
bool valid = value.canConvert( QVariant::String );
if ( ok )
{
*ok = valid;
}
if ( valid )
{
return value.toString();
}
return QString( def );
} // QgsProject::readEntry
int
QgsProject::readNumEntry( QString const &scope, const QString & key, int def,
bool * ok ) const
{
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
QVariant value;
if ( property )
{
value = property->value();
}
bool valid = value.canConvert( QVariant::String );
if ( ok )
{
*ok = valid;
}
if ( valid )
{
return value.toInt();
}
return def;
} // QgsProject::readNumEntry
double
QgsProject::readDoubleEntry( QString const &scope, const QString & key,
double def,
bool * ok ) const
{
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
QVariant value;
if ( property )
{
value = property->value();
}
bool valid = value.canConvert( QVariant::Double );
if ( ok )
{
*ok = valid;
}
if ( valid )
{
return value.toDouble();
}
return def;
} // QgsProject::readDoubleEntry
bool
QgsProject::readBoolEntry( QString const &scope, const QString & key, bool def,
bool * ok ) const
{
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
QVariant value;
if ( property )
{
value = property->value();
}
bool valid = value.canConvert( QVariant::Bool );
if ( ok )
{
*ok = valid;
}
if ( valid )
{
return value.toBool();
}
return def;
} // QgsProject::readBoolEntry
bool QgsProject::removeEntry( QString const &scope, const QString & key )
{
removeKey_( scope, key, imp_->properties_ );
dirty( true );
return ! findKey_( scope, key, imp_->properties_ );
} // QgsProject::removeEntry
QStringList QgsProject::entryList( QString const &scope, QString const &key ) const
{
QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
QStringList entries;
if ( foundProperty )
{
QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
if ( propertyKey )
{ propertyKey->entryList( entries ); }
}
return entries;
} // QgsProject::entryList
QStringList
QgsProject::subkeyList( QString const &scope, QString const &key ) const
{
QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
QStringList entries;
if ( foundProperty )
{
QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
if ( propertyKey )
{ propertyKey->subkeyList( entries ); }
}
return entries;
} // QgsProject::subkeyList
void QgsProject::dumpProperties() const
{
dump_( imp_->properties_ );
} // QgsProject::dumpProperties