1
/***************************************************************************
2
qgsproject.cpp - description
5
copyright : (C) 2004 by Mark Coletti
6
email : mcoletti at gmail.com
7
***************************************************************************/
9
/***************************************************************************
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. *
16
***************************************************************************/
18
#include "qgsproject.h"
26
#include "qgslegend.h"
28
#include "qgsvectorlayer.h"
29
#include "qgsrasterlayer.h"
30
#include "qgsmaplayerregistry.h"
31
#include "qgsexception.h"
32
#include "qgsprojectproperty.h"
33
#include "qgsmapcanvas.h"
34
#include "qgslogger.h"
36
#include <QApplication>
41
#include <QTextStream>
46
static const char *const ident_ = "$Id: qgsproject.cpp 7019 2007-06-11 03:55:32Z timlinux $";
52
// / canonical project instance
53
QgsProject * QgsProject::theProject_;
62
Take the given scope and key and convert them to a string list of key
63
tokens that will be used to navigate through a Property hierarchy
65
E.g., scope "someplugin" and key "/foo/bar/baz" will become a string list
66
of { "properties", "someplugin", "foo", "bar", "baz" }. "properties" is
67
always first because that's the permanent ``root'' Property node.
70
QStringList makeKeyTokens_(QString const &scope, QString const &key)
72
// XXX - debugger probes
73
// const char * scope_str = scope.toLocal8Bit().data();
74
// const char * key_str = key.toLocal8Bit().data();
76
QStringList keyTokens = QStringList(scope);
77
keyTokens += QStringList::split('/', key);
79
// be sure to include the canonical root node
80
keyTokens.push_front( "properties" );
89
return the property that matches the given key sequence, if any
91
@param sequence is a tokenized key sequence
92
@param p is likely to be the top level QgsPropertyKey in QgsProject:e:Imp.
94
@return null if not found, otherwise located Property
97
QgsProperty * findKey_( QString const & scope,
99
QgsPropertyKey & rootProperty )
101
QgsPropertyKey * currentProperty = &rootProperty;
102
QgsProperty * nextProperty; // link to next property down hiearchy
104
QStringList keySequence = makeKeyTokens_( scope, key );
106
while ( ! keySequence.isEmpty() )
108
// if the current head of the sequence list matches the property name,
109
// then traverse down the property hierarchy
110
if ( keySequence.first() == currentProperty->name() )
112
// remove front key since we're traversing down a level
113
keySequence.pop_front();
115
// if we have only one key name left, then return the key found
116
if ( 1 == keySequence.count() )
118
return currentProperty->find( keySequence.front() );
121
// if we're out of keys then the current property is the one we
122
// want; i.e., we're in the rate case of being at the top-most
124
else if ( keySequence.isEmpty() )
126
return currentProperty;
128
else if (( nextProperty = currentProperty->find( keySequence.first() ) ))
130
if ( nextProperty->isKey() )
132
currentProperty = dynamic_cast<QgsPropertyKey*>(nextProperty);
134
// it may be that this may be one of several property value
135
// nodes keyed by QDict string; if this is the last remaining
136
// key token and the next property is a value node, then
137
// that's the situation, so return the currentProperty
138
else if ( nextProperty->isValue() && (1 == keySequence.count()) )
140
return currentProperty;
142
else // QgsPropertyValue not Key, so return null
147
else // if the next key down isn't found
148
{ // then the overall key sequence doesn't exist
163
/** add the given key and value
165
@param sequence is list of keys
166
@param rootProperty is the property from which to start adding
167
@param value the value associated with the key
170
QgsProperty * addKey_( QString const & scope,
172
QgsPropertyKey * rootProperty,
175
QStringList keySequence = makeKeyTokens_( scope, key );
177
// cursor through property key/value hierarchy
178
QgsPropertyKey * currentProperty = rootProperty;
180
QgsProperty * newProperty; // link to next property down hiearchy
182
while ( ! keySequence.isEmpty() )
184
// if the current head of the sequence list matches the property name,
185
// then traverse down the property hierarchy
186
if ( keySequence.first() == currentProperty->name() )
188
// remove front key since we're traversing down a level
189
keySequence.pop_front();
191
// if key sequence has one last element, then we use that as the
192
// name to store the value
193
if ( 1 == keySequence.count() )
195
currentProperty->setValue( keySequence.front(), value );
196
return currentProperty;
198
// we're at the top element if popping the keySequence element
199
// will leave it empty; in that case, just add the key
200
else if ( keySequence.isEmpty() )
202
currentProperty->setValue( value );
204
return currentProperty;
206
else if (( newProperty = currentProperty->find( keySequence.first() ) ))
208
currentProperty = dynamic_cast<QgsPropertyKey*>(newProperty);
210
if ( currentProperty )
214
else // QgsPropertyValue not Key, so return null
219
else // the next subkey doesn't exist, so add it
221
newProperty = currentProperty->addKey( keySequence.first() );
225
currentProperty = dynamic_cast<QgsPropertyKey*>(newProperty);
243
void removeKey_(QString const & scope,
245
QgsPropertyKey & rootProperty)
247
QgsPropertyKey * currentProperty = &rootProperty;
249
QgsProperty * nextProperty; // link to next property down hiearchy
250
QgsPropertyKey * previousQgsPropertyKey; // link to previous property up hiearchy
252
QStringList keySequence = makeKeyTokens_( scope, key );
254
while ( ! keySequence.isEmpty() )
256
// if the current head of the sequence list matches the property name,
257
// then traverse down the property hierarchy
258
if ( keySequence.first() == currentProperty->name() )
260
// remove front key since we're traversing down a level
261
keySequence.pop_front();
263
// if we have only one key name left, then try to remove the key
265
if ( 1 == keySequence.count() )
267
currentProperty->removeKey( keySequence.front() );
269
// if we're out of keys then the current property is the one we
270
// want to remove, but we can't delete it directly; we need to
271
// delete it from the parent property key container
272
else if ( keySequence.isEmpty() )
274
previousQgsPropertyKey->removeKey( currentProperty->name() );
276
else if (( nextProperty = currentProperty->find( keySequence.first() ) ))
278
previousQgsPropertyKey = currentProperty;
279
currentProperty = dynamic_cast<QgsPropertyKey*>(nextProperty);
281
if ( currentProperty )
285
else // QgsPropertyValue not Key, so return null
290
else // if the next key down isn't found
291
{ // then the overall key sequence doesn't exist
305
struct QgsProject::Imp
307
/// current physical project file
310
/// property hierarchy
311
QgsPropertyKey properties_;
316
/// true if project has been modified since it has been read or saved
319
/// map units for current project
320
QGis::units mapUnits;
325
mapUnits(QGis::METERS)
326
{ // top property node is the root
327
// "properties" that contains all plug-in
328
// and extra property keys and values
329
properties_.name() = "properties"; // root property node always this value
332
/** clear project properties when a new project is started
336
QgsDebug( "Clearing project properties Impl->clear();" );
338
properties_.clearKeys();
339
mapUnits = QGis::METERS;
342
// reset some default project properties
343
// XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
344
QgsProject::instance()->writeEntry("PositionPrecision", "/Automatic", true);
345
QgsProject::instance()->writeEntry("PositionPrecision", "/DecimalPlaces", 2);
348
}; // struct QgsProject::Imp
352
QgsProject::QgsProject()
353
: imp_(new QgsProject::Imp)
355
// Set some default project properties
356
// XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
357
writeEntry("PositionPrecision", "/Automatic", true);
358
writeEntry("PositionPrecision", "/DecimalPlaces", 2);
359
// XXX writeEntry() makes the project dirty, but it doesn't make sense
360
// for a new project to be dirty, so let's clean it up
366
QgsProject::~QgsProject()
368
// note that std::auto_ptr automatically deletes imp_ when it's destroyed
373
QgsProject * QgsProject::instance()
375
if (!QgsProject::theProject_)
377
QgsProject::theProject_ = new QgsProject;
380
return QgsProject::theProject_;
381
} // QgsProject * instance()
386
void QgsProject::title(QString const &title)
391
} // void QgsProject::title
394
QString const & QgsProject::title() const
397
} // QgsProject::title() const
400
QGis::units QgsProject::mapUnits() const
402
return imp_->mapUnits;
403
} // QGis::units QgsProject::mapUnits() const
407
void QgsProject::mapUnits(QGis::units u)
412
} // void QgsProject::mapUnits(QGis::units u)
415
bool QgsProject::dirty() const
418
} // bool QgsProject::dirty()
421
void QgsProject::dirty(bool b)
424
} // bool QgsProject::dirty()
428
void QgsProject::filename(QString const &name)
430
imp_->file.setName(name);
433
} // void QgsProject::filename( QString const & name )
437
QString QgsProject::filename() const
439
return imp_->file.name();
440
} // QString QgsProject::filename() const
444
/// basically a debugging tool to dump property list values
445
static void dump_( QgsPropertyKey const & topQgsPropertyKey )
447
qDebug("current properties:");
449
topQgsPropertyKey.dump();
455
Fetches extents, or area of interest, saved in project.
457
@note extent XML of form:
459
<xmin>[number]</xmin>
460
<ymin>[number]</ymin>
461
<xmax>[number]</xmax>
462
<ymax>[number]</ymax>
466
static bool _getExtents(QDomDocument const &doc, QgsRect & aoi)
468
QDomNodeList extents = doc.elementsByTagName("extent");
470
if (extents.count() > 1)
473
("there appears to be more than one extent in the project\nusing first one");
474
} else if (extents.count() < 1) // no extents found, so bail
479
QDomNode extentNode = extents.item(0);
481
QDomNode xminNode = extentNode.namedItem("xmin");
482
QDomNode yminNode = extentNode.namedItem("ymin");
483
QDomNode xmaxNode = extentNode.namedItem("xmax");
484
QDomNode ymaxNode = extentNode.namedItem("ymax");
486
QDomElement exElement = xminNode.toElement();
487
double xmin = exElement.text().toDouble();
490
exElement = yminNode.toElement();
491
double ymin = exElement.text().toDouble();
494
exElement = xmaxNode.toElement();
495
double xmax = exElement.text().toDouble();
498
exElement = ymaxNode.toElement();
499
double ymax = exElement.text().toDouble();
510
Restore any optional properties found in "doc" to "properties".
512
<properties> tags for all optional properties. Within that there will be
513
scope tags. In the following example there exist one property in the
514
"fsplugin" scope. "layers" is a list containing three string values.
518
<foo type="int" >42</foo>
519
<baz type="int" >1</baz>
520
<layers type="QStringList" >
521
<value>railroad</value>
522
<value>airport</value>
524
<xyqzzy type="int" >1</xyqzzy>
525
<bar type="double" >123.456</bar>
526
<feature_types type="QStringList" >
532
@param project_properties should be the top QgsPropertyKey node.
537
_getProperties(QDomDocument const &doc, QgsPropertyKey & project_properties)
539
QDomNodeList properties = doc.elementsByTagName("properties");
541
if (properties.count() > 1)
543
qDebug("there appears to be more than one ``properties'' XML tag ... bailing");
545
} else if (properties.count() < 1) // no properties found, so we're done
550
// item(0) because there should only be ONE
552
QDomNodeList scopes = properties.item(0).childNodes();
554
if (scopes.count() < 1)
556
qDebug("empty ``properties'' XML tag ... bailing");
560
QDomNode propertyNode = properties.item(0);
562
if ( ! project_properties.readXML( propertyNode ) )
564
qDebug("Project_properties.readXML() failed");
567
// DEPRECATED as functionality has been shoved down to QgsProperyKey::readXML()
570
// while (i < scopes.count())
572
// QDomNode curr_scope_node = scopes.item(i);
574
// qDebug("found %d property node(s) for scope %s",
575
// curr_scope_node.childNodes().count(),
576
// (const char *) curr_scope_node.nodeName().utf8());
578
// QString key(curr_scope_node.nodeName());
580
// QgsPropertyKey * currentKey =
581
// dynamic_cast<QgsPropertyKey*>(project_properties.find( key ));
583
// if ( ! currentKey )
585
// // if the property key doesn't yet exist, create an empty instance
588
// currentKey = project_properties.addKey( key );
590
// if ( ! currentKey )
592
// qDebug( "%s:%d unable to add key", __FILE__, __LINE__ );
596
// if (! currentKey->readXML(curr_scope_node))
598
// qDebug("%s:%d unable to read XML for property %s", __FILE__, __LINE__,
599
// (const char *) curr_scope_node.nodeName().utf8());
609
Get the project map units
611
XML in file has this form:
614
static bool _getMapUnits(QDomDocument const &doc)
616
QDomNodeList nl = doc.elementsByTagName("units");
618
// since "units" is a new project file type, legacy project files will not
619
// have this element. If we do have such a legacy project file missing
620
// "units", then just return;
626
QDomNode node = nl.item(0); // there should only be one, so zeroth element ok
627
QDomElement element = node.toElement();
629
if ("meters" == element.text())
631
QgsProject::instance()->mapUnits(QGis::METERS);
632
} else if ("feet" == element.text())
634
QgsProject::instance()->mapUnits(QGis::FEET);
635
} else if ("degrees" == element.text())
637
QgsProject::instance()->mapUnits(QGis::DEGREES);
638
} else if ("unknown" == element.text())
640
QgsProject::instance()->mapUnits(QGis::UNKNOWN);
644
cerr << __FILE__ << ":" << __LINE__ << " unknown map unit type " <<
645
element.text().toLocal8Bit().data() << "\n";
656
Get the project title
658
XML in file has this form:
659
<qgis projectname="default project">
660
<title>a project title</title>
662
@todo XXX we should go with the attribute xor <title>, not both.
664
static void _getTitle(QDomDocument const &doc, QString & title)
666
QDomNodeList nl = doc.elementsByTagName("title");
668
title = ""; // by default the title will be empty
672
qDebug("%s : %d %s", __FILE__, __LINE__, " unable to find title element\n");
676
QDomNode titleNode = nl.item(0); // there should only be one, so zeroth element ok
678
if (!titleNode.hasChildNodes()) // if not, then there's no actual text
680
qDebug("%s : %d %s", __FILE__, __LINE__, " unable to find title element\n");
684
QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
686
if (!titleTextNode.isText())
688
qDebug("%s : %d %s", __FILE__, __LINE__, " unable to find title element\n");
692
QDomText titleText = titleTextNode.toText();
694
title = titleText.data();
699
/** return the version string found in the given DOM document
701
@returns the version string or an empty string if none found
703
static QString _getVersion(QDomDocument const &doc)
705
QDomNodeList nl = doc.elementsByTagName("qgis");
709
QgsDebug(" unable to find qgis element in project file");
713
QDomNode qgisNode = nl.item(0); // there should only be one, so zeroth element ok
715
QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
717
return qgisElement.attribute("version");
722
locate a qgsMapCanvas object
724
static QgsMapCanvas * _findMapCanvas()
726
QgsMapCanvas * theMapCanvas = 0x0;
727
QString canonicalMapCanvasName = "theMapCanvas";
729
QWidgetList wlist = QApplication::topLevelWidgets();
730
foreach (QWidget *widget, QApplication::topLevelWidgets())
732
theMapCanvas = dynamic_cast <QgsMapCanvas *>(widget->child(canonicalMapCanvasName));
745
qDebug("Unable to find the map canvas widget with name " + canonicalMapCanvasName);
747
return 0x0; // XXX some sort of error value? Exception?
753
static QgsLegend * _findLegend()
755
QString canonicalLegendName = "theMapLegend";
756
QgsLegend* theLegend = 0;
758
QWidgetList wlist = QApplication::topLevelWidgets();
759
foreach (QWidget *widget, QApplication::topLevelWidgets())
761
theLegend = dynamic_cast <QgsLegend *>(widget->child(canonicalLegendName.toLocal8Bit().data(), 0, true));
775
Read map layers from project file
778
@returns pair of < bool, list<QDomNode> >; bool is true if function worked;
779
else is false. list contains nodes corresponding to layers that couldn't
784
<maplayer type="vector" visible="1" showInOverviewFlag="0">
785
<layername>Hydrop</layername>
786
<datasource>/data/usgs/city_shp/hydrop.shp</datasource>
788
<provider>ogr</provider>
791
<value>blabla</value>
793
<outlinecolor red="85" green="0" blue="255" />
794
<outlinestyle>SolidLine</outlinestyle>
795
<outlinewidth>1</outlinewidth>
796
<fillcolor red="0" green="170" blue="255" />
797
<fillpattern>SolidPattern</fillpattern>
799
<label>blabla</label>
804
<label text="Label" field="" />
805
<family name="Sans Serif" field="" />
806
<size value="12" units="pt" field="" />
807
<bold on="0" field="" />
808
<italic on="0" field="" />
809
<underline on="0" field="" />
810
<color red="0" green="0" blue="0" field="" />
813
<offset units="pt" x="0" xfield="" y="0" yfield="" />
814
<angle value="0" field="" />
815
<alignment value="center" field="" />
820
static pair< bool, list<QDomNode> > _getMapLayers(QDomDocument const &doc)
822
// Layer order is implicit in the order they are stored in the project file
824
QDomNodeList nl = doc.elementsByTagName("maplayer");
826
// XXX what is this used for? QString layerCount( QString::number(nl.count()) );
830
list<QDomNode> brokenNodes; // a list of DOM nodes corresponding to layers
831
// that we were unable to load; this could be
832
// because the layers were removed or
833
// re-located after the project was last saved
835
// process the map layer nodes
837
if (0 == nl.count()) // if we have no layers to process, bail
839
return make_pair(true, brokenNodes); // Decided to return "true" since it's
840
// possible for there to be a project with no
841
// layers; but also, more imporantly, this
842
// would cause the tests/qgsproject to fail
843
// since the test suite doesn't currently
844
// support test layers
847
bool returnStatus = true;
849
for (int i = 0; i < nl.count(); i++)
851
QDomNode node = nl.item(i);
852
QDomElement element = node.toElement();
854
QString type = element.attribute("type");
857
QgsMapLayer *mapLayer;
860
std::cerr << "type is " << type.toLocal8Bit().data() << std::endl;
863
if (type == "vector")
865
mapLayer = new QgsVectorLayer;
866
} else if (type == "raster")
868
mapLayer = new QgsRasterLayer;
871
Q_CHECK_PTR(mapLayer);
876
std::cerr << __FILE__ << " : " << __LINE__ << " unable to create layer\n";
878
return make_pair(false, brokenNodes);
881
// have the layer restore state that is stored in DOM node
882
if ( mapLayer->readXML(node) )
884
mapLayer = QgsMapLayerRegistry::instance()->addMapLayer(mapLayer);
885
mapLayer->refreshLegend();
891
qDebug( "%s:%d unable to load %s layer", __FILE__, __LINE__, (const char *)type.toLocal8Bit().data() );
893
returnStatus = false; // flag that we had problems loading layers
895
brokenNodes.push_back( node );
899
return make_pair(returnStatus, brokenNodes);
905
Sets the given canvas' extents
908
static void _setCanvasExtent(QgsRect const &newExtent)
910
// first find the canonical map canvas
911
QgsMapCanvas *theMapCanvas = _findMapCanvas();
915
// Don't produce an error message here because _findMapCanvas
917
return; // XXX some sort of error value? Exception?
920
theMapCanvas->setExtent(newExtent);
922
// XXX sometimes the canvases are frozen here, sometimes not; this is a
923
// XXX worrisome inconsistency; regardless, unfreeze the canvases to ensure
925
theMapCanvas->freeze(false);
927
theMapCanvas->update();
929
} // _setCanvasExtent()
934
Get the full extent for the given canvas.
936
This is used to get the full extent of the main map canvas so that we can
937
set the overview canvas to that instead of stupidly setting the overview
938
canvas to the *same* extent that's in the main map canvas.
941
static QgsRect _getFullExtent()
943
// XXX since this is a cut-n-paste from above, maybe generalize to a
944
// XXX separate function?
945
// first find the canonical map canvas
946
QgsMapCanvas *theMapCanvas = _findMapCanvas();
950
// _findMapCanvas() will produce an error if the map canvas wasn't found
951
return QgsRect(); // XXX some sort of error value? Exception?
955
return theMapCanvas->fullExtent();
957
} // _getFullExtent( )
965
Get the extent for the given canvas.
967
This is used to get the extent of the main map canvas so that we can
968
set the overview canvas to that instead of stupidly setting the overview
969
canvas to the *same* extent that's in the main map canvas.
972
static QgsRect _getExtent()
974
// XXX since this is a cut-n-paste from above, maybe generalize to a
975
// XXX separate function?
976
// first find the canonical map canvas
977
QgsMapCanvas *theMapCanvas = _findMapCanvas();
981
// _findMapCanvas will produce an error if the map canvas wasn't found
982
return QgsRect(); // XXX some sort of error value? Exception?
986
return theMapCanvas->extent();
994
Please note that most of the contents were copied from qgsproject
996
bool QgsProject::read(QFileInfo const &file)
998
imp_->file.setName(file.filePath());
1001
} // QgsProject::read( QFile & file )
1006
@note it's presumed that the caller has already reset the map canvas, map
1007
registry, and legend
1009
bool QgsProject::read()
1011
std::auto_ptr< QDomDocument > doc =
1012
std::auto_ptr < QDomDocument > (new QDomDocument("qgis"));
1014
if (!imp_->file.open(QIODevice::ReadOnly))
1016
imp_->file.close(); // even though we got an error, let's make
1017
// sure it's closed anyway
1019
throw QgsIOException( QObject::tr("Unable to open ") + imp_->file.name());
1021
return false; // XXX raise exception? Ok now superfluous
1022
// XXX because of exception.
1025
// location of problem associated with errorMsg
1029
if (!doc->setContent(&imp_->file, &errorMsg, &line, &column))
1031
// want to make this class as GUI independent as possible; so commented out
1032
// QMessageBox::critical( 0x0, "Project File Read Error",
1033
// errorMsg + " at line " + QString::number( line ) +
1034
// " column " + QString::number( column ) );
1036
QString errorString = QObject::tr("Project file read error: ") +
1037
errorMsg + QObject::tr(" at line ") + QString::number(line) + QObject::tr(" column ") +
1038
QString::number(column);
1040
qDebug((const char *) errorString.utf8());
1044
throw QgsException(errorString + QObject::tr(" for file ") + imp_->file.name());
1046
return false; // XXX superfluous because of exception
1053
qWarning(("opened document " + imp_->file.name()).toLocal8Bit().data());
1056
// before we start loading everything, let's clear out the current set of
1057
// properties first so that we don't have the properties from the previous
1058
// project still hanging around
1062
// now get any properties
1063
_getProperties(*doc, imp_->properties_);
1065
qDebug("%s:%d %d properties read", __FILE__, __LINE__,
1066
imp_->properties_.count());
1068
dump_(imp_->properties_);
1071
// restore the canvas' area of interest
1073
// now get project title
1074
_getTitle(*doc, imp_->title);
1076
// get project version string, if any
1077
QString fileVersion = _getVersion(*doc);
1079
if ( fileVersion.isNull() )
1081
QgsDebug( "project file has no version string" );
1085
QgsDebug( QString("project file has version " + fileVersion).ascii() );
1088
QStringList fileVersionParts = fileVersion.split(".");
1089
QStringList qgisVersionParts = QString(QGis::qgisVersion).split(".");
1093
if (fileVersionParts.size() != 3 || qgisVersionParts.size() != 3)
1094
older = false; // probably an older version
1097
if (fileVersionParts.at(0) < qgisVersionParts.at(0))
1099
else if (fileVersionParts.at(1) < qgisVersionParts.at(1))
1101
else if (fileVersionParts.at(2) < qgisVersionParts.at(2))
1107
QgsLogger::warning("Loading a file that was saved with an older "
1108
"version of qgis (saved in " + fileVersion +
1109
", loaded in " + QGis::qgisVersion +
1110
"). Problems may occur.");
1114
qDebug(("Project title: " + imp_->title).toLocal8Bit().data());
1117
// now set the map units; note, alters QgsProject::instance().
1119
QgsMapCanvas* canvas = _findMapCanvas();
1121
canvas->setMapUnits(mapUnits());
1123
// get the map layers
1124
pair< bool, list<QDomNode> > getMapLayersResults = _getMapLayers(*doc);
1126
// Restore the map canvas extent
1128
QgsRect savedExtent;
1130
if (!_getExtents(*doc, savedExtent))
1133
qDebug("Unable to get extents from project file.");
1136
// Since we could be executing this from the test harness which
1137
// doesn't *have* layers -- nor a GUI for that matter -- we'll just
1138
// leave in the whining and boldly stomp on.
1140
throw QgsException( QObject::tr("Cannot get extents from ") + imp_->file.name());
1148
canvas->setExtent(savedExtent);
1152
// review the integrity of the retrieved map layers
1154
if ( ! getMapLayersResults.first )
1157
qDebug("Unable to get map layers from project file.");
1160
if ( ! getMapLayersResults.second.empty() )
1162
qDebug( "%s:%d there are %d broken layers", __FILE__, __LINE__, getMapLayersResults.second.size() );
1165
// Since we could be executing this from the test harness which
1166
// doesn't *have* layers -- nor a GUI for that matter -- we'll just
1167
// leave in the whining and boldly stomp on.
1169
//also restore the legend before bailing out
1170
QgsLegend* theLegend = _findLegend();
1173
QDomNodeList ll = doc->elementsByTagName("legend");
1176
QDomNode legendnode = ll.item(0);
1177
theLegend->readXML(legendnode);
1182
throw QgsProjectBadLayerException( getMapLayersResults.second );
1189
QgsLegend* theLegend = _findLegend();
1192
QDomNodeList ll = doc->elementsByTagName("legend");
1195
QDomNode legendnode = ll.item(0);
1196
theLegend->readXML(legendnode);
1200
// can't be dirty since we're allegedly in pristine state
1205
} // QgsProject::read
1211
bool QgsProject::read( QDomNode & layerNode )
1213
QString type = layerNode.toElement().attribute("type");
1215
QgsMapLayer *mapLayer;
1217
if (type == "vector")
1219
mapLayer = new QgsVectorLayer;
1221
else if (type == "raster")
1223
mapLayer = new QgsRasterLayer;
1228
std::cerr << __FILE__ << " : " << __LINE__ << " bad layer type\n";
1236
std::cerr << __FILE__ << " : " << __LINE__ << " unable to create layer\n";
1241
// have the layer restore state that is stored in DOM node
1242
if ( mapLayer->readXML(layerNode) )
1244
mapLayer = QgsMapLayerRegistry::instance()->addMapLayer(mapLayer);
1250
qDebug( "%s:%d unable to load %s layer", __FILE__, __LINE__, (const char *)type.toLocal8Bit().data() );
1256
} // QgsProject::read( QDomNode & layerNode )
1260
bool QgsProject::write(QFileInfo const &file)
1262
imp_->file.setName(file.filePath());
1265
} // QgsProject::write( QFileInfo const & file )
1268
bool QgsProject::write()
1270
// if we have problems creating or otherwise writing to the project file,
1271
// let's find out up front before we go through all the hand-waving
1272
// necessary to create all the DOM objects
1273
if (!imp_->file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
1275
imp_->file.close(); // even though we got an error, let's make
1276
// sure it's closed anyway
1278
throw QgsIOException(QObject::tr("Unable to save to file ") + imp_->file.name());
1280
return false; // XXX raise exception? Ok now superfluous
1281
// XXX because of exception.
1283
QFileInfo myFileInfo(imp_->file);
1284
if (!myFileInfo.isWritable())
1286
// even though we got an error, let's make
1287
// sure it's closed anyway
1289
throw QgsIOException(imp_->file.name() + QObject::tr(QString(" is not writeable.")
1290
+ QString("Please adjust permissions (if possible) and try again.")));
1291
// XXX raise exception? Ok now superfluous
1296
QDomImplementation DOMImplementation;
1298
QDomDocumentType documentType =
1299
DOMImplementation.createDocumentType("qgis", "http://mrcc.com/qgis.dtd",
1301
std::auto_ptr < QDomDocument > doc =
1302
std::auto_ptr < QDomDocument > (new QDomDocument(documentType));
1305
QDomElement qgisNode = doc->createElement("qgis");
1306
qgisNode.setAttribute("projectname", title());
1307
qgisNode.setAttribute("version", QString("%1").arg(QGis::qgisVersion));
1309
doc->appendChild(qgisNode);
1312
QDomElement titleNode = doc->createElement("title");
1313
qgisNode.appendChild(titleNode);
1315
QDomText titleText = doc->createTextNode(title()); // XXX why have title TWICE?
1316
titleNode.appendChild(titleText);
1320
QDomElement unitsNode = doc->createElement("units");
1321
qgisNode.appendChild(unitsNode);
1323
QString unitsString;
1325
switch (instance()->imp_->mapUnits)
1328
unitsString = "meters";
1331
unitsString = "feet";
1334
unitsString = "degrees";
1338
unitsString = "unknown";
1342
QDomText unitsText = doc->createTextNode(unitsString);
1343
unitsNode.appendChild(unitsText);
1345
// extents and layers info are written by the map canvas
1346
// find the canonical map canvas
1347
QgsMapCanvas *theMapCanvas = _findMapCanvas();
1351
// Actually this might be run from the test harness, and therefore
1352
// there won't be a GUI, so no map canvas. Just blithely continue on.
1356
theMapCanvas->writeXML(qgisNode, *doc);
1359
//save legend settings
1360
QgsLegend* theLegend = _findLegend();
1363
theLegend->writeXML(qgisNode, *doc);
1364
//theLegend->saveToProject();
1367
// now add the optional extra properties
1369
dump_(imp_->properties_);
1371
qDebug("there are %d property scopes", imp_->properties_.count());
1373
if (!imp_->properties_.isEmpty()) // only worry about properties if we
1374
// actually have any properties
1376
imp_->properties_.writeXML( "properties", qgisNode, *doc );
1379
// now wrap it up and ship it to the project file
1380
doc->normalize(); // XXX I'm not entirely sure what this does
1382
//QString xml = doc->toString(4); // write to string with indentation of four characters
1383
// (yes, four is arbitrary)
1385
// const char * xmlString = xml; // debugger probe point
1386
// qDebug( "project file output:\n\n" + xml );
1388
QTextStream projectFileStream(&imp_->file);
1390
//projectFileStream << xml << endl;
1391
doc->save(projectFileStream, 4); // save as utf-8
1395
dirty(false); // reset to pristine state
1398
} // QgsProject::write
1402
void QgsProject::clearProperties()
1405
std::cout << "Clearing project properties QgsProject::clearProperties()" << std::endl;
1410
} // QgsProject::clearProperties()
1415
QgsProject::writeEntry(QString const &scope, const QString & key, bool value)
1419
return addKey_( scope, key, &imp_->properties_, value );
1420
} // QgsProject::writeEntry ( ..., bool value )
1424
QgsProject::writeEntry(QString const &scope, const QString & key,
1429
return addKey_( scope, key, &imp_->properties_, value );
1430
} // QgsProject::writeEntry ( ..., double value )
1434
QgsProject::writeEntry(QString const &scope, const QString & key, int value)
1438
return addKey_( scope, key, &imp_->properties_, value );
1439
} // QgsProject::writeEntry ( ..., int value )
1443
QgsProject::writeEntry(QString const &scope, const QString & key,
1444
const QString & value)
1448
return addKey_( scope, key, &imp_->properties_, value );
1449
} // QgsProject::writeEntry ( ..., const QString & value )
1453
QgsProject::writeEntry(QString const &scope, const QString & key,
1454
const QStringList & value)
1458
return addKey_( scope, key, &imp_->properties_, value );
1459
} // QgsProject::writeEntry ( ..., const QStringList & value )
1465
QgsProject::readListEntry(QString const & scope,
1466
const QString & key,
1469
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1475
value = property->value();
1478
bool valid = QVariant::StringList == value.type();
1487
return value.asStringList();
1490
return QStringList();
1491
} // QgsProject::readListEntry
1495
QgsProject::readEntry(QString const & scope,
1496
const QString & key,
1497
const QString & def,
1500
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1506
value = property->value();
1509
bool valid = value.canCast(QVariant::String);
1518
return value.toString();
1521
return QString(def);
1522
} // QgsProject::readEntry
1526
QgsProject::readNumEntry(QString const &scope, const QString & key, int def,
1529
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1535
value = property->value();
1538
bool valid = value.canCast(QVariant::String);
1547
return value.toInt();
1551
} // QgsProject::readNumEntry
1555
QgsProject::readDoubleEntry(QString const &scope, const QString & key,
1559
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1565
value = property->value();
1568
bool valid = value.canCast(QVariant::Double);
1577
return value.toDouble();
1581
} // QgsProject::readDoubleEntry
1585
QgsProject::readBoolEntry(QString const &scope, const QString & key, bool def,
1588
QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1594
value = property->value();
1597
bool valid = value.canCast(QVariant::Bool);
1606
return value.toBool();
1610
} // QgsProject::readBoolEntry
1613
bool QgsProject::removeEntry(QString const &scope, const QString & key)
1615
removeKey_( scope, key, imp_->properties_ );
1619
return ! findKey_(scope, key, imp_->properties_ );
1620
} // QgsProject::removeEntry
1624
QStringList QgsProject::entryList(QString const &scope, QString const &key) const
1626
QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1628
QStringList entries;
1630
if ( foundProperty )
1632
QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>(foundProperty);
1635
{ propertyKey->entryList( entries ); }
1639
} // QgsProject::entryList
1643
QgsProject::subkeyList(QString const &scope, QString const &key) const
1645
QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1647
QStringList entries;
1649
if ( foundProperty )
1651
QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>(foundProperty);
1654
{ propertyKey->subkeyList( entries ); }
1659
} // QgsProject::subkeyList
1663
void QgsProject::dumpProperties() const
1665
dump_(imp_->properties_);
1666
} // QgsProject::dumpProperties