~ubuntu-branches/ubuntu/natty/qgis/natty

« back to all changes in this revision

Viewing changes to src/gui/qgsproject.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Johan Van de Wauw
  • Date: 2010-07-11 20:23:24 UTC
  • mfrom: (3.1.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100711202324-5ktghxa7hracohmr
Tags: 1.4.0+12730-3ubuntu1
* Merge from Debian unstable (LP: #540941).
* Fix compilation issues with QT 4.7
* Add build-depends on libqt4-webkit-dev 

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 <memory>
21
 
#include <cassert>
22
 
#include <iostream>
23
 
 
24
 
using namespace std;
25
 
 
26
 
#include "qgslegend.h"
27
 
#include "qgsrect.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"
35
 
 
36
 
#include <QApplication>
37
 
#include <QFileInfo>
38
 
#include <QDomNode>
39
 
#include <QWidget>
40
 
#include <QObject>
41
 
#include <QTextStream>
42
 
#include <QToolBox>
43
 
#include <QSplitter>
44
 
 
45
 
 
46
 
static const char *const ident_ = "$Id: qgsproject.cpp 7019 2007-06-11 03:55:32Z timlinux $";
47
 
 
48
 
 
49
 
 
50
 
 
51
 
 
52
 
// / canonical project instance
53
 
QgsProject * QgsProject::theProject_;
54
 
 
55
 
 
56
 
 
57
 
 
58
 
 
59
 
 
60
 
 
61
 
/**
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
64
 
 
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.
68
 
 */
69
 
 static 
70
 
 QStringList makeKeyTokens_(QString const &scope, QString const &key)
71
 
 {
72
 
   // XXX - debugger probes
73
 
   // const char * scope_str = scope.toLocal8Bit().data();
74
 
   // const char * key_str   = key.toLocal8Bit().data();
75
 
 
76
 
     QStringList keyTokens = QStringList(scope);
77
 
     keyTokens += QStringList::split('/', key);
78
 
 
79
 
     // be sure to include the canonical root node
80
 
     keyTokens.push_front( "properties" );
81
 
 
82
 
     return keyTokens;
83
 
 } // makeKeyTokens_
84
 
 
85
 
 
86
 
 
87
 
 
88
 
 /**
89
 
    return the property that matches the given key sequence, if any
90
 
 
91
 
    @param sequence is a tokenized key sequence
92
 
    @param p is likely to be the top level QgsPropertyKey in QgsProject:e:Imp.
93
 
 
94
 
    @return null if not found, otherwise located Property
95
 
 */
96
 
 static
97
 
 QgsProperty * findKey_( QString const & scope, 
98
 
                         QString const & key, 
99
 
                         QgsPropertyKey &   rootProperty )
100
 
 {
101
 
     QgsPropertyKey * currentProperty = &rootProperty;
102
 
     QgsProperty * nextProperty;           // link to next property down hiearchy
103
 
 
104
 
     QStringList keySequence = makeKeyTokens_( scope, key );
105
 
 
106
 
     while ( ! keySequence.isEmpty() )
107
 
     {
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() )
111
 
         {
112
 
             // remove front key since we're traversing down a level
113
 
             keySequence.pop_front();
114
 
 
115
 
             // if we have only one key name left, then return the key found
116
 
             if ( 1 == keySequence.count() )
117
 
             {
118
 
                 return currentProperty->find( keySequence.front() );
119
 
                 
120
 
             }
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
123
 
             // property node
124
 
             else if ( keySequence.isEmpty() )
125
 
             {
126
 
                 return currentProperty;
127
 
             }
128
 
             else if (( nextProperty = currentProperty->find( keySequence.first() ) ))
129
 
             {
130
 
                 if ( nextProperty->isKey() )
131
 
                 {
132
 
                     currentProperty = dynamic_cast<QgsPropertyKey*>(nextProperty);
133
 
                 }
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()) )
139
 
                 {
140
 
                     return currentProperty;
141
 
                 }
142
 
                 else            // QgsPropertyValue not Key, so return null
143
 
                 {
144
 
                     return 0x0;
145
 
                 }
146
 
             }
147
 
             else                // if the next key down isn't found
148
 
             {                   // then the overall key sequence doesn't exist
149
 
                 return 0x0;
150
 
             }
151
 
         }
152
 
         else
153
 
         {
154
 
             return 0x0;
155
 
         }
156
 
     }
157
 
 
158
 
     return 0x0;
159
 
 } // findKey_
160
 
 
161
 
 
162
 
 
163
 
 /** add the given key and value
164
 
 
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
168
 
 */
169
 
 static
170
 
 QgsProperty * addKey_( QString const & scope,
171
 
                        QString const & key, 
172
 
                        QgsPropertyKey * rootProperty,
173
 
                        QVariant value )
174
 
 {
175
 
     QStringList keySequence = makeKeyTokens_( scope, key );
176
 
 
177
 
     // cursor through property key/value hierarchy
178
 
     QgsPropertyKey * currentProperty = rootProperty;
179
 
 
180
 
     QgsProperty * newProperty; // link to next property down hiearchy
181
 
 
182
 
     while ( ! keySequence.isEmpty() )
183
 
     {
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() )
187
 
         {
188
 
             // remove front key since we're traversing down a level
189
 
             keySequence.pop_front();
190
 
 
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() )
194
 
             {
195
 
                 currentProperty->setValue( keySequence.front(), value );
196
 
                 return currentProperty;
197
 
             }
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() )
201
 
             {
202
 
                 currentProperty->setValue( value );
203
 
 
204
 
                 return currentProperty;
205
 
             }
206
 
             else if (( newProperty = currentProperty->find( keySequence.first() ) ))
207
 
             {
208
 
                 currentProperty = dynamic_cast<QgsPropertyKey*>(newProperty);
209
 
 
210
 
                 if ( currentProperty )
211
 
                 {
212
 
                     continue;
213
 
                 }
214
 
                 else            // QgsPropertyValue not Key, so return null
215
 
                 {
216
 
                     return 0x0;
217
 
                 }
218
 
             }
219
 
             else                // the next subkey doesn't exist, so add it
220
 
             {
221
 
                 newProperty = currentProperty->addKey( keySequence.first() );
222
 
 
223
 
                 if ( newProperty )
224
 
                 {
225
 
                     currentProperty = dynamic_cast<QgsPropertyKey*>(newProperty);
226
 
                 }
227
 
                 continue;
228
 
             }
229
 
         }
230
 
         else
231
 
         {
232
 
             return 0x0;
233
 
         }
234
 
     }
235
 
 
236
 
     return 0x0;
237
 
 
238
 
 } // addKey_
239
 
 
240
 
 
241
 
 
242
 
 static
243
 
 void removeKey_(QString const & scope,
244
 
                 QString const & key, 
245
 
                 QgsPropertyKey & rootProperty)
246
 
 {
247
 
     QgsPropertyKey * currentProperty = &rootProperty;
248
 
 
249
 
     QgsProperty * nextProperty;   // link to next property down hiearchy
250
 
     QgsPropertyKey * previousQgsPropertyKey; // link to previous property up hiearchy
251
 
 
252
 
     QStringList keySequence = makeKeyTokens_( scope, key );
253
 
 
254
 
     while ( ! keySequence.isEmpty() )
255
 
     {
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() )
259
 
         {
260
 
             // remove front key since we're traversing down a level
261
 
             keySequence.pop_front();
262
 
 
263
 
             // if we have only one key name left, then try to remove the key
264
 
             // with that name
265
 
             if ( 1 == keySequence.count() )
266
 
             {
267
 
                 currentProperty->removeKey( keySequence.front() );
268
 
             }
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() )
273
 
             {
274
 
                 previousQgsPropertyKey->removeKey( currentProperty->name() );
275
 
             }
276
 
             else if (( nextProperty = currentProperty->find( keySequence.first() ) ))
277
 
             {
278
 
                 previousQgsPropertyKey = currentProperty;
279
 
                 currentProperty = dynamic_cast<QgsPropertyKey*>(nextProperty);
280
 
 
281
 
                 if ( currentProperty )
282
 
                 {
283
 
                     continue;
284
 
                 }
285
 
                 else            // QgsPropertyValue not Key, so return null
286
 
                 {
287
 
                     return;
288
 
                 }
289
 
             }
290
 
             else                // if the next key down isn't found
291
 
             {                   // then the overall key sequence doesn't exist
292
 
                 return;
293
 
             }
294
 
         }
295
 
         else
296
 
         {
297
 
             return;
298
 
         }
299
 
     }
300
 
 
301
 
 } // void removeKey_
302
 
 
303
 
 
304
 
 
305
 
 struct QgsProject::Imp
306
 
 {
307
 
     /// current physical project file
308
 
     QFile file;
309
 
 
310
 
     /// property hierarchy
311
 
     QgsPropertyKey properties_;
312
 
 
313
 
     /// project title
314
 
     QString title;
315
 
 
316
 
     /// true if project has been modified since it has been read or saved
317
 
     bool dirty;
318
 
 
319
 
     /// map units for current project
320
 
     QGis::units mapUnits;
321
 
 
322
 
     Imp()
323
 
         : title(""), 
324
 
           dirty(false), 
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
330
 
     }
331
 
 
332
 
     /** clear project properties when a new project is started 
333
 
      */ 
334
 
     void clear()
335
 
     {
336
 
         QgsDebug( "Clearing project properties Impl->clear();" );
337
 
 
338
 
         properties_.clearKeys();
339
 
         mapUnits = QGis::METERS;
340
 
         title = "";
341
 
 
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);
346
 
     }
347
 
 
348
 
 };  // struct QgsProject::Imp
349
 
 
350
 
 
351
 
 
352
 
 QgsProject::QgsProject()
353
 
     : imp_(new QgsProject::Imp)
354
 
 {
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
361
 
     dirty(false);
362
 
 } // QgsProject ctor
363
 
 
364
 
 
365
 
 
366
 
 QgsProject::~QgsProject()
367
 
 {
368
 
     // note that std::auto_ptr automatically deletes imp_ when it's destroyed
369
 
 } // QgsProject dtor
370
 
 
371
 
 
372
 
 
373
 
 QgsProject * QgsProject::instance()
374
 
 {
375
 
     if (!QgsProject::theProject_)
376
 
     {
377
 
         QgsProject::theProject_ = new QgsProject;
378
 
     }
379
 
 
380
 
     return QgsProject::theProject_;
381
 
 } // QgsProject * instance()
382
 
 
383
 
 
384
 
 
385
 
 
386
 
 void QgsProject::title(QString const &title)
387
 
 {
388
 
     imp_->title = title;
389
 
 
390
 
     dirty(true);
391
 
 } // void QgsProject::title
392
 
 
393
 
 
394
 
 QString const & QgsProject::title() const
395
 
 {
396
 
     return imp_->title;
397
 
 } // QgsProject::title() const
398
 
 
399
 
 
400
 
 QGis::units QgsProject::mapUnits() const
401
 
 {
402
 
     return imp_->mapUnits;
403
 
 } // QGis::units QgsProject::mapUnits() const
404
 
 
405
 
 
406
 
 
407
 
 void QgsProject::mapUnits(QGis::units u)
408
 
 {
409
 
     imp_->mapUnits = u;
410
 
 
411
 
     dirty(true);
412
 
 } // void QgsProject::mapUnits(QGis::units u)
413
 
 
414
 
 
415
 
 bool QgsProject::dirty() const
416
 
 {
417
 
     return imp_->dirty;
418
 
 } // bool QgsProject::dirty() 
419
 
 
420
 
 
421
 
 void QgsProject::dirty(bool b)
422
 
 {
423
 
     imp_->dirty = b;
424
 
 } // bool QgsProject::dirty()
425
 
 
426
 
 
427
 
 
428
 
 void QgsProject::filename(QString const &name)
429
 
 {
430
 
     imp_->file.setName(name);
431
 
 
432
 
     dirty(true);
433
 
 } // void QgsProject::filename( QString const & name )
434
 
 
435
 
 
436
 
 
437
 
 QString QgsProject::filename() const
438
 
 {
439
 
     return imp_->file.name();
440
 
 } // QString QgsProject::filename() const
441
 
 
442
 
 
443
 
 
444
 
 /// basically a debugging tool to dump property list values 
445
 
 static void dump_( QgsPropertyKey const & topQgsPropertyKey )
446
 
 {
447
 
     qDebug("current properties:");
448
 
 
449
 
     topQgsPropertyKey.dump();
450
 
 } // dump_
451
 
 
452
 
 
453
 
 
454
 
 /**
455
 
    Fetches extents, or area of interest, saved in project.
456
 
 
457
 
    @note extent XML of form:
458
 
    <extent>
459
 
    <xmin>[number]</xmin>
460
 
    <ymin>[number]</ymin>
461
 
    <xmax>[number]</xmax>
462
 
    <ymax>[number]</ymax>
463
 
    </extent>
464
 
 
465
 
 */
466
 
 static bool _getExtents(QDomDocument const &doc, QgsRect & aoi)
467
 
 {
468
 
     QDomNodeList extents = doc.elementsByTagName("extent");
469
 
 
470
 
     if (extents.count() > 1)
471
 
     {
472
 
         qDebug
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
475
 
     {
476
 
         return false;
477
 
     }
478
 
 
479
 
     QDomNode extentNode = extents.item(0);
480
 
 
481
 
     QDomNode xminNode = extentNode.namedItem("xmin");
482
 
     QDomNode yminNode = extentNode.namedItem("ymin");
483
 
     QDomNode xmaxNode = extentNode.namedItem("xmax");
484
 
     QDomNode ymaxNode = extentNode.namedItem("ymax");
485
 
 
486
 
     QDomElement exElement = xminNode.toElement();
487
 
     double xmin = exElement.text().toDouble();
488
 
     aoi.setXmin(xmin);
489
 
 
490
 
     exElement = yminNode.toElement();
491
 
     double ymin = exElement.text().toDouble();
492
 
     aoi.setYmin(ymin);
493
 
 
494
 
     exElement = xmaxNode.toElement();
495
 
     double xmax = exElement.text().toDouble();
496
 
     aoi.setXmax(xmax);
497
 
 
498
 
     exElement = ymaxNode.toElement();
499
 
     double ymax = exElement.text().toDouble();
500
 
     aoi.setYmax(ymax);
501
 
 
502
 
     return true;
503
 
 
504
 
 }                               // _getExtents
505
 
 
506
 
 
507
 
 
508
 
 /**
509
 
 
510
 
 Restore any optional properties found in "doc" to "properties".
511
 
 
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.
515
 
 
516
 
 <properties>
517
 
   <fsplugin>
518
 
     <foo type="int" >42</foo>
519
 
     <baz type="int" >1</baz>
520
 
     <layers type="QStringList" >
521
 
       <value>railroad</value>
522
 
       <value>airport</value>
523
 
     </layers>
524
 
     <xyqzzy type="int" >1</xyqzzy>
525
 
     <bar type="double" >123.456</bar>
526
 
     <feature_types type="QStringList" >
527
 
        <value>type</value>
528
 
     </feature_types>
529
 
   </fsplugin>
530
 
 </properties>
531
 
 
532
 
 @param project_properties should be the top QgsPropertyKey node.
533
 
 
534
 
 */
535
 
static 
536
 
void
537
 
_getProperties(QDomDocument const &doc, QgsPropertyKey & project_properties)
538
 
{
539
 
    QDomNodeList properties = doc.elementsByTagName("properties");
540
 
 
541
 
    if (properties.count() > 1)
542
 
    {
543
 
        qDebug("there appears to be more than one ``properties'' XML tag ... bailing");
544
 
        return;
545
 
    } else if (properties.count() < 1)  // no properties found, so we're done
546
 
    {
547
 
        return;
548
 
    }
549
 
 
550
 
    // item(0) because there should only be ONE
551
 
    // "properties" node
552
 
    QDomNodeList scopes = properties.item(0).childNodes();
553
 
 
554
 
    if (scopes.count() < 1)
555
 
    {
556
 
        qDebug("empty ``properties'' XML tag ... bailing");
557
 
        return;
558
 
    }
559
 
 
560
 
    QDomNode propertyNode = properties.item(0);
561
 
 
562
 
    if ( ! project_properties.readXML( propertyNode ) )
563
 
    {
564
 
        qDebug("Project_properties.readXML() failed");
565
 
    }
566
 
 
567
 
// DEPRECATED as functionality has been shoved down to QgsProperyKey::readXML()
568
 
 
569
 
//     size_t i = 0;
570
 
//     while (i < scopes.count())
571
 
//     {
572
 
//         QDomNode curr_scope_node = scopes.item(i);
573
 
 
574
 
//         qDebug("found %d property node(s) for scope %s",
575
 
//                curr_scope_node.childNodes().count(),
576
 
//                (const char *) curr_scope_node.nodeName().utf8());
577
 
 
578
 
//         QString key(curr_scope_node.nodeName());
579
 
 
580
 
//         QgsPropertyKey * currentKey = 
581
 
//             dynamic_cast<QgsPropertyKey*>(project_properties.find( key ));
582
 
 
583
 
//         if ( ! currentKey )
584
 
//         {
585
 
//             // if the property key doesn't yet exist, create an empty instance
586
 
//             // of that key
587
 
 
588
 
//             currentKey = project_properties.addKey( key );
589
 
 
590
 
//             if ( ! currentKey )
591
 
//             {
592
 
//                 qDebug( "%s:%d unable to add key", __FILE__, __LINE__ );
593
 
//             }
594
 
//         }
595
 
 
596
 
//         if (! currentKey->readXML(curr_scope_node))
597
 
//         {
598
 
//             qDebug("%s:%d unable to read XML for property %s", __FILE__, __LINE__,
599
 
//                    (const char *) curr_scope_node.nodeName().utf8());
600
 
//         }
601
 
 
602
 
//         ++i;
603
 
//     }
604
 
} // _getProperties
605
 
 
606
 
 
607
 
 
608
 
/**
609
 
   Get the project map units
610
 
 
611
 
   XML in file has this form:
612
 
   <units>feet</units>
613
 
*/
614
 
static bool _getMapUnits(QDomDocument const &doc)
615
 
{
616
 
    QDomNodeList nl = doc.elementsByTagName("units");
617
 
 
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;
621
 
    if (!nl.count())
622
 
    {
623
 
        return false;
624
 
    }
625
 
 
626
 
    QDomNode node = nl.item(0);   // there should only be one, so zeroth element ok
627
 
    QDomElement element = node.toElement();
628
 
 
629
 
    if ("meters" == element.text())
630
 
    {
631
 
        QgsProject::instance()->mapUnits(QGis::METERS);
632
 
    } else if ("feet" == element.text())
633
 
    {
634
 
        QgsProject::instance()->mapUnits(QGis::FEET);
635
 
    } else if ("degrees" == element.text())
636
 
    {
637
 
        QgsProject::instance()->mapUnits(QGis::DEGREES);
638
 
    } else if ("unknown" == element.text())
639
 
    {
640
 
        QgsProject::instance()->mapUnits(QGis::UNKNOWN);
641
 
    } else
642
 
    {
643
 
        std::
644
 
            cerr << __FILE__ << ":" << __LINE__ << " unknown map unit type " <<
645
 
            element.text().toLocal8Bit().data() << "\n";
646
 
        false;
647
 
    }
648
 
 
649
 
    return true;
650
 
 
651
 
} // _getMapUnits
652
 
 
653
 
 
654
 
 
655
 
/**
656
 
   Get the project title
657
 
 
658
 
   XML in file has this form:
659
 
   <qgis projectname="default project">
660
 
   <title>a project title</title>
661
 
 
662
 
   @todo XXX we should go with the attribute xor <title>, not both.
663
 
*/
664
 
static void _getTitle(QDomDocument const &doc, QString & title)
665
 
{
666
 
    QDomNodeList nl = doc.elementsByTagName("title");
667
 
 
668
 
    title = "";                 // by default the title will be empty
669
 
 
670
 
    if (!nl.count())
671
 
    {
672
 
        qDebug("%s : %d %s", __FILE__, __LINE__, " unable to find title element\n");
673
 
        return;
674
 
    }
675
 
 
676
 
    QDomNode titleNode = nl.item(0);  // there should only be one, so zeroth element ok
677
 
 
678
 
    if (!titleNode.hasChildNodes()) // if not, then there's no actual text
679
 
    {
680
 
        qDebug("%s : %d %s", __FILE__, __LINE__, " unable to find title element\n");
681
 
        return;
682
 
    }
683
 
 
684
 
    QDomNode titleTextNode = titleNode.firstChild();  // should only have one child
685
 
 
686
 
    if (!titleTextNode.isText())
687
 
    {
688
 
        qDebug("%s : %d %s", __FILE__, __LINE__, " unable to find title element\n");
689
 
        return;
690
 
    }
691
 
 
692
 
    QDomText titleText = titleTextNode.toText();
693
 
 
694
 
    title = titleText.data();
695
 
 
696
 
} // _getTitle
697
 
 
698
 
 
699
 
/** return the version string found in the given DOM document
700
 
 
701
 
   @returns the version string or an empty string if none found
702
 
 */
703
 
static QString _getVersion(QDomDocument const &doc)
704
 
{
705
 
    QDomNodeList nl = doc.elementsByTagName("qgis");
706
 
 
707
 
    if (!nl.count())
708
 
    {
709
 
        QgsDebug(" unable to find qgis element in project file");
710
 
        return "";
711
 
    }
712
 
    
713
 
    QDomNode qgisNode = nl.item(0);  // there should only be one, so zeroth element ok
714
 
 
715
 
    QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
716
 
 
717
 
    return qgisElement.attribute("version");
718
 
} // _getVersion
719
 
 
720
 
 
721
 
/**
722
 
   locate a qgsMapCanvas object
723
 
*/
724
 
static QgsMapCanvas * _findMapCanvas()
725
 
{
726
 
  QgsMapCanvas * theMapCanvas = 0x0;
727
 
  QString canonicalMapCanvasName = "theMapCanvas";
728
 
 
729
 
  QWidgetList wlist = QApplication::topLevelWidgets();
730
 
  foreach (QWidget *widget, QApplication::topLevelWidgets()) 
731
 
  {
732
 
    theMapCanvas = dynamic_cast <QgsMapCanvas *>(widget->child(canonicalMapCanvasName)); 
733
 
    if (theMapCanvas)
734
 
    {
735
 
      break;
736
 
    }
737
 
  }
738
 
 
739
 
  if (theMapCanvas)
740
 
  {
741
 
    return theMapCanvas;
742
 
  } 
743
 
  else
744
 
  {
745
 
    qDebug("Unable to find the map canvas widget with name " + canonicalMapCanvasName);
746
 
 
747
 
    return 0x0;                 // XXX some sort of error value? Exception?
748
 
  }
749
 
} // _findMapCanvas
750
 
 
751
 
 
752
 
 
753
 
static QgsLegend * _findLegend()
754
 
{
755
 
  QString canonicalLegendName = "theMapLegend";
756
 
  QgsLegend* theLegend = 0;
757
 
 
758
 
  QWidgetList wlist = QApplication::topLevelWidgets();
759
 
  foreach (QWidget *widget, QApplication::topLevelWidgets()) 
760
 
  {
761
 
    theLegend = dynamic_cast <QgsLegend *>(widget->child(canonicalLegendName.toLocal8Bit().data(), 0, true)); 
762
 
    if(theLegend)
763
 
      {
764
 
        break;
765
 
      }
766
 
  }
767
 
  
768
 
  return theLegend;
769
 
 
770
 
} // _findLegend
771
 
 
772
 
 
773
 
 
774
 
/**
775
 
   Read map layers from project file
776
 
 
777
 
   
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
780
 
            be loaded
781
 
 
782
 
   @note XML of form:
783
 
 
784
 
   <maplayer type="vector" visible="1" showInOverviewFlag="0">
785
 
      <layername>Hydrop</layername>
786
 
      <datasource>/data/usgs/city_shp/hydrop.shp</datasource>
787
 
      <zorder>0</zorder>
788
 
      <provider>ogr</provider>
789
 
      <singlesymbol>
790
 
         <renderitem>
791
 
            <value>blabla</value>
792
 
            <symbol>
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>
798
 
            </symbol>
799
 
            <label>blabla</label>
800
 
         </renderitem>
801
 
      </singlesymbol>
802
 
      <label>0</label>
803
 
      <labelattributes>
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="" />
811
 
         <x field="" />
812
 
         <y field="" />
813
 
         <offset  units="pt" x="0" xfield="" y="0" yfield="" />
814
 
         <angle value="0" field="" />
815
 
         <alignment value="center" field="" />
816
 
      </labelattributes>
817
 
   </maplayer>
818
 
 
819
 
*/
820
 
static pair< bool, list<QDomNode> > _getMapLayers(QDomDocument const &doc)
821
 
{
822
 
    // Layer order is implicit in the order they are stored in the project file
823
 
 
824
 
    QDomNodeList nl = doc.elementsByTagName("maplayer");
825
 
 
826
 
    // XXX what is this used for? QString layerCount( QString::number(nl.count()) );
827
 
 
828
 
    QString wk;
829
 
 
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
834
 
 
835
 
    // process the map layer nodes
836
 
 
837
 
    if (0 == nl.count())        // if we have no layers to process, bail
838
 
    {
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
845
 
    }
846
 
 
847
 
    bool returnStatus = true;
848
 
 
849
 
    for (int i = 0; i < nl.count(); i++)
850
 
    {
851
 
        QDomNode node = nl.item(i);
852
 
        QDomElement element = node.toElement();
853
 
 
854
 
        QString type = element.attribute("type");
855
 
 
856
 
 
857
 
        QgsMapLayer *mapLayer;
858
 
#ifdef QGISDEBUG
859
 
 
860
 
        std::cerr << "type is " << type.toLocal8Bit().data() << std::endl;
861
 
#endif
862
 
 
863
 
        if (type == "vector")
864
 
        {
865
 
            mapLayer = new QgsVectorLayer;
866
 
        } else if (type == "raster")
867
 
        {
868
 
            mapLayer = new QgsRasterLayer;
869
 
        }
870
 
 
871
 
        Q_CHECK_PTR(mapLayer);
872
 
 
873
 
        if (!mapLayer)
874
 
        {
875
 
#ifdef QGISDEBUG
876
 
            std::cerr << __FILE__ << " : " << __LINE__ << " unable to create layer\n";
877
 
#endif
878
 
            return make_pair(false, brokenNodes);
879
 
        }
880
 
 
881
 
        // have the layer restore state that is stored in DOM node
882
 
        if ( mapLayer->readXML(node) )
883
 
        {
884
 
            mapLayer = QgsMapLayerRegistry::instance()->addMapLayer(mapLayer);
885
 
            mapLayer->refreshLegend();
886
 
        }
887
 
        else
888
 
        {
889
 
            delete mapLayer;
890
 
 
891
 
            qDebug( "%s:%d unable to load %s layer", __FILE__, __LINE__, (const char *)type.toLocal8Bit().data() );
892
 
 
893
 
            returnStatus = false; // flag that we had problems loading layers
894
 
 
895
 
            brokenNodes.push_back( node );
896
 
        }
897
 
    }
898
 
 
899
 
    return make_pair(returnStatus, brokenNodes);
900
 
 
901
 
} // _getMapLayers
902
 
 
903
 
 
904
 
/**
905
 
   Sets the given canvas' extents
906
 
 
907
 
*/
908
 
static void _setCanvasExtent(QgsRect const &newExtent)
909
 
{
910
 
    // first find the canonical map canvas
911
 
    QgsMapCanvas *theMapCanvas = _findMapCanvas();
912
 
 
913
 
    if (!theMapCanvas)
914
 
    {
915
 
      // Don't produce an error message here because _findMapCanvas
916
 
      // does one already.
917
 
      return;                     // XXX some sort of error value? Exception?
918
 
    }
919
 
 
920
 
    theMapCanvas->setExtent(newExtent);
921
 
 
922
 
    // XXX sometimes the canvases are frozen here, sometimes not; this is a
923
 
    // XXX worrisome inconsistency; regardless, unfreeze the canvases to ensure
924
 
    // XXX a redraw
925
 
    theMapCanvas->freeze(false);
926
 
    
927
 
    theMapCanvas->update();
928
 
 
929
 
}                               // _setCanvasExtent()
930
 
 
931
 
 
932
 
 
933
 
/**
934
 
   Get the full extent for the given canvas.
935
 
 
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.
939
 
 
940
 
*/
941
 
static QgsRect _getFullExtent()
942
 
{
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();
947
 
 
948
 
    if (!theMapCanvas)
949
 
    {
950
 
      // _findMapCanvas() will produce an error if the map canvas wasn't found
951
 
        return QgsRect();           // XXX some sort of error value? Exception?
952
 
    }
953
 
 
954
 
 
955
 
    return theMapCanvas->fullExtent();
956
 
 
957
 
} // _getFullExtent( )
958
 
 
959
 
 
960
 
 
961
 
 
962
 
 
963
 
 
964
 
/**
965
 
   Get the  extent for the given canvas.
966
 
 
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.
970
 
 
971
 
*/
972
 
static QgsRect _getExtent()
973
 
{
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();
978
 
 
979
 
    if (!theMapCanvas)
980
 
    {
981
 
      // _findMapCanvas will produce an error if the map canvas wasn't found
982
 
        return QgsRect();           // XXX some sort of error value? Exception?
983
 
    }
984
 
 
985
 
 
986
 
    return theMapCanvas->extent();
987
 
 
988
 
} // _getExtent( )
989
 
 
990
 
 
991
 
 
992
 
 
993
 
/**
994
 
   Please note that most of the contents were copied from qgsproject
995
 
*/
996
 
bool QgsProject::read(QFileInfo const &file)
997
 
{
998
 
    imp_->file.setName(file.filePath());
999
 
 
1000
 
    return read();
1001
 
} // QgsProject::read( QFile & file )
1002
 
 
1003
 
 
1004
 
 
1005
 
/**
1006
 
   @note it's presumed that the caller has already reset the map canvas, map
1007
 
   registry, and legend
1008
 
*/
1009
 
bool QgsProject::read()
1010
 
{
1011
 
    std::auto_ptr< QDomDocument > doc =
1012
 
        std::auto_ptr < QDomDocument > (new QDomDocument("qgis"));
1013
 
 
1014
 
    if (!imp_->file.open(QIODevice::ReadOnly))
1015
 
    {
1016
 
        imp_->file.close();     // even though we got an error, let's make
1017
 
                                // sure it's closed anyway
1018
 
 
1019
 
        throw QgsIOException( QObject::tr("Unable to open ") + imp_->file.name());
1020
 
 
1021
 
        return false;           // XXX raise exception? Ok now superfluous
1022
 
                                // XXX because of exception.
1023
 
    }
1024
 
 
1025
 
    // location of problem associated with errorMsg
1026
 
    int line, column;
1027
 
    QString errorMsg;
1028
 
 
1029
 
    if (!doc->setContent(&imp_->file, &errorMsg, &line, &column))
1030
 
    {
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 ) );
1035
 
 
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);
1039
 
 
1040
 
        qDebug((const char *) errorString.utf8());
1041
 
 
1042
 
        imp_->file.close();
1043
 
 
1044
 
        throw QgsException(errorString + QObject::tr(" for file ") + imp_->file.name());
1045
 
 
1046
 
        return false;               // XXX superfluous because of exception
1047
 
    }
1048
 
 
1049
 
    imp_->file.close();
1050
 
 
1051
 
 
1052
 
#ifdef QGISDEBUG
1053
 
    qWarning(("opened document " + imp_->file.name()).toLocal8Bit().data());
1054
 
#endif
1055
 
 
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
1059
 
 
1060
 
    imp_->clear();
1061
 
 
1062
 
    // now get any properties
1063
 
    _getProperties(*doc, imp_->properties_);
1064
 
 
1065
 
    qDebug("%s:%d %d properties read", __FILE__, __LINE__,
1066
 
           imp_->properties_.count());
1067
 
 
1068
 
    dump_(imp_->properties_);
1069
 
 
1070
 
 
1071
 
    // restore the canvas' area of interest
1072
 
 
1073
 
    // now get project title
1074
 
    _getTitle(*doc, imp_->title);
1075
 
 
1076
 
    // get project version string, if any
1077
 
    QString fileVersion = _getVersion(*doc);
1078
 
 
1079
 
    if ( fileVersion.isNull() )
1080
 
    {
1081
 
        QgsDebug( "project file has no version string" );
1082
 
    }
1083
 
    else
1084
 
    {
1085
 
        QgsDebug( QString("project file has version " + fileVersion).ascii() );
1086
 
    }
1087
 
 
1088
 
    QStringList fileVersionParts = fileVersion.split(".");
1089
 
    QStringList qgisVersionParts = QString(QGis::qgisVersion).split(".");
1090
 
 
1091
 
    bool older = false;
1092
 
 
1093
 
    if (fileVersionParts.size() != 3 || qgisVersionParts.size() != 3)
1094
 
      older = false; // probably an older version
1095
 
    else
1096
 
    {
1097
 
      if (fileVersionParts.at(0) < qgisVersionParts.at(0))
1098
 
        older = true;
1099
 
      else if (fileVersionParts.at(1) < qgisVersionParts.at(1))
1100
 
        older = true;
1101
 
      else if (fileVersionParts.at(2) < qgisVersionParts.at(2))
1102
 
        older = true;
1103
 
    }
1104
 
 
1105
 
    if (older)
1106
 
    {
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.");
1111
 
    }
1112
 
 
1113
 
#ifdef QGISDEBUG
1114
 
    qDebug(("Project title: " + imp_->title).toLocal8Bit().data());
1115
 
#endif
1116
 
 
1117
 
    // now set the map units; note, alters QgsProject::instance().
1118
 
    _getMapUnits(*doc);
1119
 
    QgsMapCanvas* canvas = _findMapCanvas();
1120
 
    if (canvas)
1121
 
      canvas->setMapUnits(mapUnits());
1122
 
 
1123
 
    // get the map layers
1124
 
    pair< bool, list<QDomNode> > getMapLayersResults =  _getMapLayers(*doc);
1125
 
 
1126
 
    // Restore the map canvas extent
1127
 
 
1128
 
    QgsRect savedExtent;
1129
 
 
1130
 
    if (!_getExtents(*doc, savedExtent))
1131
 
    {
1132
 
#ifdef QGISDEBUG
1133
 
        qDebug("Unable to get extents from project file.");
1134
 
#endif
1135
 
 
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.
1139
 
 
1140
 
         throw QgsException( QObject::tr("Cannot get extents from ") + imp_->file.name());
1141
 
 
1142
 
         // return false;
1143
 
    }
1144
 
    else
1145
 
    {
1146
 
      if (canvas)
1147
 
      {
1148
 
        canvas->setExtent(savedExtent);
1149
 
      }
1150
 
    }
1151
 
 
1152
 
    // review the integrity of the retrieved map layers
1153
 
 
1154
 
    if ( ! getMapLayersResults.first )
1155
 
    {
1156
 
#ifdef QGISDEBUG
1157
 
        qDebug("Unable to get map layers from project file.");
1158
 
#endif
1159
 
 
1160
 
        if ( ! getMapLayersResults.second.empty() )
1161
 
        {
1162
 
            qDebug( "%s:%d there are %d broken layers", __FILE__, __LINE__, getMapLayersResults.second.size() );
1163
 
        }
1164
 
 
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.
1168
 
 
1169
 
        //also restore the legend before bailing out
1170
 
        QgsLegend* theLegend = _findLegend();
1171
 
        if(theLegend)
1172
 
          {
1173
 
            QDomNodeList ll = doc->elementsByTagName("legend");
1174
 
            if(ll.count()==1)
1175
 
              {
1176
 
                QDomNode legendnode = ll.item(0);
1177
 
                theLegend->readXML(legendnode);
1178
 
              }
1179
 
          
1180
 
          }
1181
 
 
1182
 
        throw QgsProjectBadLayerException( getMapLayersResults.second );
1183
 
 
1184
 
//         return false;
1185
 
    }
1186
 
 
1187
 
    //restore legend
1188
 
      
1189
 
    QgsLegend* theLegend = _findLegend();
1190
 
    if(theLegend)
1191
 
    {
1192
 
      QDomNodeList ll = doc->elementsByTagName("legend");
1193
 
      if(ll.count()==1)
1194
 
      {
1195
 
        QDomNode legendnode = ll.item(0);
1196
 
        theLegend->readXML(legendnode);
1197
 
      }
1198
 
    }
1199
 
 
1200
 
    // can't be dirty since we're allegedly in pristine state
1201
 
    dirty(false);
1202
 
 
1203
 
    return true;
1204
 
 
1205
 
} // QgsProject::read
1206
 
 
1207
 
 
1208
 
 
1209
 
 
1210
 
 
1211
 
bool QgsProject::read( QDomNode & layerNode )
1212
 
{
1213
 
    QString type = layerNode.toElement().attribute("type");
1214
 
 
1215
 
    QgsMapLayer *mapLayer;
1216
 
 
1217
 
    if (type == "vector")
1218
 
    {
1219
 
        mapLayer = new QgsVectorLayer;
1220
 
    } 
1221
 
    else if (type == "raster")
1222
 
    {
1223
 
        mapLayer = new QgsRasterLayer;
1224
 
    }
1225
 
    else
1226
 
    {
1227
 
#ifdef QGISDEBUG
1228
 
        std::cerr << __FILE__ << " : " << __LINE__ << " bad layer type\n";
1229
 
#endif
1230
 
        return false;
1231
 
    }
1232
 
 
1233
 
    if (!mapLayer)
1234
 
    {
1235
 
#ifdef QGISDEBUG
1236
 
        std::cerr << __FILE__ << " : " << __LINE__ << " unable to create layer\n";
1237
 
#endif
1238
 
        return false;
1239
 
    }
1240
 
 
1241
 
    // have the layer restore state that is stored in DOM node
1242
 
    if ( mapLayer->readXML(layerNode) )
1243
 
    {
1244
 
        mapLayer = QgsMapLayerRegistry::instance()->addMapLayer(mapLayer);
1245
 
    }
1246
 
    else
1247
 
    {
1248
 
        delete mapLayer;
1249
 
 
1250
 
        qDebug( "%s:%d unable to load %s layer", __FILE__, __LINE__, (const char *)type.toLocal8Bit().data() );
1251
 
 
1252
 
        return false;
1253
 
    }
1254
 
 
1255
 
    return  true;
1256
 
} // QgsProject::read( QDomNode & layerNode )
1257
 
 
1258
 
 
1259
 
 
1260
 
bool QgsProject::write(QFileInfo const &file)
1261
 
{
1262
 
    imp_->file.setName(file.filePath());
1263
 
 
1264
 
    return write();
1265
 
} // QgsProject::write( QFileInfo const & file )
1266
 
 
1267
 
 
1268
 
bool QgsProject::write()
1269
 
{
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))
1274
 
  {
1275
 
    imp_->file.close();         // even though we got an error, let's make
1276
 
    // sure it's closed anyway
1277
 
 
1278
 
    throw QgsIOException(QObject::tr("Unable to save to file ") + imp_->file.name());
1279
 
 
1280
 
    return false;               // XXX raise exception? Ok now superfluous
1281
 
    // XXX because of exception.
1282
 
  }
1283
 
  QFileInfo myFileInfo(imp_->file);
1284
 
  if (!myFileInfo.isWritable())
1285
 
  {
1286
 
    // even though we got an error, let's make
1287
 
    // sure it's closed anyway
1288
 
    imp_->file.close();         
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
1292
 
    return false;               
1293
 
   
1294
 
  }
1295
 
 
1296
 
  QDomImplementation DOMImplementation;
1297
 
 
1298
 
  QDomDocumentType documentType =
1299
 
      DOMImplementation.createDocumentType("qgis", "http://mrcc.com/qgis.dtd",
1300
 
              "SYSTEM");
1301
 
  std::auto_ptr < QDomDocument > doc =
1302
 
      std::auto_ptr < QDomDocument > (new QDomDocument(documentType));
1303
 
 
1304
 
 
1305
 
  QDomElement qgisNode = doc->createElement("qgis");
1306
 
  qgisNode.setAttribute("projectname", title());
1307
 
  qgisNode.setAttribute("version", QString("%1").arg(QGis::qgisVersion));
1308
 
 
1309
 
  doc->appendChild(qgisNode);
1310
 
 
1311
 
  // title
1312
 
  QDomElement titleNode = doc->createElement("title");
1313
 
  qgisNode.appendChild(titleNode);
1314
 
 
1315
 
  QDomText titleText = doc->createTextNode(title());  // XXX why have title TWICE?
1316
 
  titleNode.appendChild(titleText);
1317
 
 
1318
 
  // units
1319
 
 
1320
 
  QDomElement unitsNode = doc->createElement("units");
1321
 
  qgisNode.appendChild(unitsNode);
1322
 
 
1323
 
  QString unitsString;
1324
 
 
1325
 
  switch (instance()->imp_->mapUnits)
1326
 
  {
1327
 
      case QGis::METERS:
1328
 
          unitsString = "meters";
1329
 
          break;
1330
 
      case QGis::FEET:
1331
 
          unitsString = "feet";
1332
 
          break;
1333
 
      case QGis::DEGREES:
1334
 
          unitsString = "degrees";
1335
 
          break;
1336
 
      case QGis::UNKNOWN:
1337
 
      default:
1338
 
          unitsString = "unknown";
1339
 
          break;
1340
 
  }
1341
 
 
1342
 
  QDomText unitsText = doc->createTextNode(unitsString);
1343
 
  unitsNode.appendChild(unitsText);
1344
 
 
1345
 
  // extents and layers info are written by the map canvas
1346
 
  // find the canonical map canvas
1347
 
  QgsMapCanvas *theMapCanvas = _findMapCanvas();
1348
 
 
1349
 
  if (!theMapCanvas)
1350
 
  {
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.
1353
 
  }
1354
 
  else
1355
 
  {
1356
 
    theMapCanvas->writeXML(qgisNode, *doc);
1357
 
  }
1358
 
 
1359
 
  //save legend settings
1360
 
  QgsLegend* theLegend = _findLegend();
1361
 
  if(theLegend)
1362
 
  {
1363
 
    theLegend->writeXML(qgisNode, *doc);
1364
 
    //theLegend->saveToProject();
1365
 
  }
1366
 
 
1367
 
  // now add the optional extra properties
1368
 
 
1369
 
  dump_(imp_->properties_);
1370
 
 
1371
 
  qDebug("there are %d property scopes", imp_->properties_.count());
1372
 
 
1373
 
  if (!imp_->properties_.isEmpty()) // only worry about properties if we
1374
 
    // actually have any properties
1375
 
  {
1376
 
    imp_->properties_.writeXML( "properties", qgisNode, *doc );
1377
 
  } 
1378
 
 
1379
 
  // now wrap it up and ship it to the project file
1380
 
  doc->normalize();             // XXX I'm not entirely sure what this does
1381
 
 
1382
 
  //QString xml = doc->toString(4); // write to string with indentation of four characters
1383
 
  // (yes, four is arbitrary)
1384
 
 
1385
 
  // const char * xmlString = xml; // debugger probe point
1386
 
  // qDebug( "project file output:\n\n" + xml );
1387
 
 
1388
 
  QTextStream projectFileStream(&imp_->file);
1389
 
 
1390
 
  //projectFileStream << xml << endl;
1391
 
  doc->save(projectFileStream, 4);  // save as utf-8
1392
 
  imp_->file.close();
1393
 
 
1394
 
 
1395
 
  dirty(false);                 // reset to pristine state
1396
 
 
1397
 
  return true;
1398
 
} // QgsProject::write
1399
 
 
1400
 
 
1401
 
 
1402
 
void QgsProject::clearProperties()
1403
 
{
1404
 
#ifdef QGISDEBUG
1405
 
    std::cout << "Clearing project properties QgsProject::clearProperties()" << std::endl;
1406
 
#endif
1407
 
    imp_->clear();
1408
 
 
1409
 
    dirty(true);
1410
 
} // QgsProject::clearProperties()
1411
 
 
1412
 
 
1413
 
 
1414
 
bool
1415
 
QgsProject::writeEntry(QString const &scope, const QString & key, bool value)
1416
 
{
1417
 
    dirty(true);
1418
 
 
1419
 
    return addKey_( scope, key, &imp_->properties_, value );
1420
 
} // QgsProject::writeEntry ( ..., bool value )
1421
 
 
1422
 
 
1423
 
bool
1424
 
QgsProject::writeEntry(QString const &scope, const QString & key,
1425
 
                       double value)
1426
 
{
1427
 
    dirty(true);
1428
 
 
1429
 
    return addKey_( scope, key, &imp_->properties_, value );
1430
 
} // QgsProject::writeEntry ( ..., double value )
1431
 
 
1432
 
 
1433
 
bool
1434
 
QgsProject::writeEntry(QString const &scope, const QString & key, int value)
1435
 
{
1436
 
    dirty(true);
1437
 
 
1438
 
    return addKey_( scope, key, &imp_->properties_, value );
1439
 
} // QgsProject::writeEntry ( ..., int value )
1440
 
 
1441
 
 
1442
 
bool
1443
 
QgsProject::writeEntry(QString const &scope, const QString & key,
1444
 
                       const QString & value)
1445
 
{
1446
 
    dirty(true);
1447
 
 
1448
 
    return addKey_( scope, key, &imp_->properties_, value );
1449
 
} // QgsProject::writeEntry ( ..., const QString & value )
1450
 
 
1451
 
 
1452
 
bool
1453
 
QgsProject::writeEntry(QString const &scope, const QString & key,
1454
 
                       const QStringList & value)
1455
 
{
1456
 
    dirty(true);
1457
 
 
1458
 
    return addKey_( scope, key, &imp_->properties_, value );
1459
 
} // QgsProject::writeEntry ( ..., const QStringList & value )
1460
 
 
1461
 
 
1462
 
 
1463
 
 
1464
 
QStringList
1465
 
QgsProject::readListEntry(QString const & scope,
1466
 
                          const QString & key,
1467
 
                          bool * ok) const 
1468
 
{
1469
 
    QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1470
 
 
1471
 
    QVariant value;
1472
 
 
1473
 
    if ( property )
1474
 
    {
1475
 
        value = property->value();
1476
 
    }
1477
 
 
1478
 
    bool valid = QVariant::StringList == value.type();
1479
 
 
1480
 
    if (ok)
1481
 
    {
1482
 
        *ok = valid;
1483
 
    }
1484
 
 
1485
 
    if (valid)
1486
 
    {
1487
 
        return value.asStringList();
1488
 
    }
1489
 
 
1490
 
    return QStringList();
1491
 
} // QgsProject::readListEntry
1492
 
 
1493
 
 
1494
 
QString
1495
 
QgsProject::readEntry(QString const & scope, 
1496
 
                      const QString & key,
1497
 
                      const QString & def,
1498
 
                      bool * ok) const 
1499
 
{
1500
 
    QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1501
 
 
1502
 
    QVariant value;
1503
 
 
1504
 
    if ( property )
1505
 
    {
1506
 
        value = property->value();
1507
 
    }
1508
 
 
1509
 
    bool valid = value.canCast(QVariant::String);
1510
 
 
1511
 
    if (ok)
1512
 
    {
1513
 
        *ok = valid;
1514
 
    }
1515
 
 
1516
 
    if (valid)
1517
 
    {
1518
 
        return value.toString();
1519
 
    }
1520
 
 
1521
 
    return QString(def);
1522
 
} // QgsProject::readEntry
1523
 
 
1524
 
 
1525
 
int
1526
 
QgsProject::readNumEntry(QString const &scope, const QString & key, int def,
1527
 
                         bool * ok) const 
1528
 
{
1529
 
    QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1530
 
 
1531
 
    QVariant value;
1532
 
 
1533
 
    if ( property )
1534
 
    {
1535
 
        value = property->value();
1536
 
    }
1537
 
 
1538
 
    bool valid = value.canCast(QVariant::String);
1539
 
 
1540
 
    if (ok)
1541
 
    {
1542
 
        *ok = valid;
1543
 
    }
1544
 
 
1545
 
    if (valid)
1546
 
    {
1547
 
        return value.toInt();
1548
 
    }
1549
 
 
1550
 
    return def;
1551
 
} // QgsProject::readNumEntry
1552
 
 
1553
 
 
1554
 
double
1555
 
QgsProject::readDoubleEntry(QString const &scope, const QString & key,
1556
 
                            double def,
1557
 
                            bool * ok) const 
1558
 
{
1559
 
    QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1560
 
 
1561
 
    QVariant value;
1562
 
 
1563
 
    if ( property )
1564
 
    {
1565
 
        value = property->value();
1566
 
    }
1567
 
 
1568
 
    bool valid = value.canCast(QVariant::Double);
1569
 
 
1570
 
    if (ok)
1571
 
    {
1572
 
        *ok = valid;
1573
 
    }
1574
 
 
1575
 
    if (valid)
1576
 
    {
1577
 
        return value.toDouble();
1578
 
    }
1579
 
 
1580
 
    return def;
1581
 
} // QgsProject::readDoubleEntry
1582
 
 
1583
 
 
1584
 
bool
1585
 
QgsProject::readBoolEntry(QString const &scope, const QString & key, bool def,
1586
 
                          bool * ok) const
1587
 
{
1588
 
    QgsProperty * property = findKey_( scope, key, imp_->properties_ );
1589
 
 
1590
 
    QVariant value;
1591
 
 
1592
 
    if ( property )
1593
 
    {
1594
 
        value = property->value();
1595
 
    }
1596
 
 
1597
 
    bool valid = value.canCast(QVariant::Bool);
1598
 
 
1599
 
    if (ok)
1600
 
    {
1601
 
        *ok = valid;
1602
 
    }
1603
 
 
1604
 
    if (valid)
1605
 
    {
1606
 
        return value.toBool();
1607
 
    }
1608
 
 
1609
 
    return def;
1610
 
} // QgsProject::readBoolEntry
1611
 
 
1612
 
 
1613
 
bool QgsProject::removeEntry(QString const &scope, const QString & key)
1614
 
{
1615
 
    removeKey_( scope, key, imp_->properties_ );
1616
 
 
1617
 
    dirty(true);
1618
 
 
1619
 
    return ! findKey_(scope, key, imp_->properties_ );
1620
 
} // QgsProject::removeEntry
1621
 
 
1622
 
 
1623
 
 
1624
 
QStringList QgsProject::entryList(QString const &scope, QString const &key) const
1625
 
{
1626
 
    QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1627
 
 
1628
 
    QStringList entries;
1629
 
 
1630
 
    if ( foundProperty )
1631
 
    {
1632
 
        QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>(foundProperty);
1633
 
 
1634
 
        if (propertyKey)
1635
 
        { propertyKey->entryList( entries ); }
1636
 
    }
1637
 
 
1638
 
    return entries;
1639
 
} // QgsProject::entryList 
1640
 
 
1641
 
 
1642
 
QStringList 
1643
 
QgsProject::subkeyList(QString const &scope, QString const &key) const
1644
 
{
1645
 
    QgsProperty * foundProperty = findKey_( scope, key, imp_->properties_ );
1646
 
 
1647
 
    QStringList entries;
1648
 
 
1649
 
    if ( foundProperty )
1650
 
    {
1651
 
        QgsPropertyKey * propertyKey = dynamic_cast<QgsPropertyKey*>(foundProperty);
1652
 
 
1653
 
        if (propertyKey)
1654
 
        { propertyKey->subkeyList( entries ); }
1655
 
    }
1656
 
 
1657
 
    return entries;
1658
 
 
1659
 
} // QgsProject::subkeyList 
1660
 
 
1661
 
 
1662
 
 
1663
 
void QgsProject::dumpProperties() const
1664
 
{
1665
 
    dump_(imp_->properties_);
1666
 
} // QgsProject::dumpProperties