QGIS API Documentation 3.99.0-Master (a8882ad4560)
Loading...
Searching...
No Matches
qgsprojectproperty.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsproject.cpp - description
3 -------------------
4 begin : February 24, 2005
5 copyright : (C) 2005 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 "qgsprojectproperty.h"
19
20#include "qgis.h"
21#include "qgslogger.h"
22#include "qgsmessagelog.h"
23
24#include <QDomDocument>
25#include <QStringList>
26
30
31void QgsProjectPropertyValue::dump( int tabs ) const
32{
33 Q_UNUSED( tabs )
34#ifdef QGISDEBUG
35
36 QString tabString;
37 tabString.fill( '\t', tabs );
38
39 if ( QMetaType::Type::QStringList == mValue.userType() )
40 {
41 const QStringList sl = mValue.toStringList();
42
43 for ( const auto &string : sl )
44 {
45 QgsDebugMsgLevel( u"%1[%2] "_s.arg( tabString, string ), 4 );
46 }
47 }
48 else
49 {
50 QgsDebugMsgLevel( u"%1%2"_s.arg( tabString, mValue.toString() ), 4 );
51 }
52#endif
53}
54
55bool QgsProjectPropertyValue::readXml( const QDomNode &keyNode )
56{
57 // this *should* be a Dom element node
58 QDomElement subkeyElement = keyNode.toElement();
59
60 // get the type so that we can properly parse the key value
61 QString typeString = subkeyElement.attribute( u"type"_s );
62
63 if ( typeString.isNull() )
64 {
65 QgsDebugError( u"null ``type'' attribute for %1"_s.arg( keyNode.nodeName() ) );
66
67 return false;
68 }
69
70 // the values come in as strings; we need to restore them to their
71 // original values *and* types
72 mValue.clear();
73
74 // get the type associated with the value first
75 QMetaType::Type type = static_cast<QMetaType::Type>( QMetaType::fromName( typeString.toLocal8Bit().constData() ).id() );
76
77 // This huge switch is left-over from an earlier incarnation of
78 // QgsProject where there was a fine level of granularity for value
79 // types. The current interface, borrowed from QgsSettings, supports a
80 // very small sub-set of these types. However, I've left all the
81 // other types just in case the interface is expanded to include these
82 // other types.
83
84 switch ( type )
85 {
86 case QMetaType::Type::UnknownType:
87 QgsDebugError( u"invalid value type %1 .. "_s.arg( typeString ) );
88 return false;
89
90 case QMetaType::Type::QVariantMap:
91 QgsDebugError( u"no support for QVariant::Map"_s );
92 return false;
93
94 case QMetaType::Type::QVariantList:
95 QgsDebugError( u"no support for QVariant::List"_s );
96 return false;
97
98 case QMetaType::Type::QString:
99 mValue = subkeyElement.text(); // no translating necessary
100 break;
101
102 case QMetaType::Type::QStringList:
103 {
104 int i = 0;
105 QDomNodeList values = keyNode.childNodes();
106
107 // all the QStringList values will be inside <value> elements
108 QStringList valueStringList;
109
110 while ( i < values.count() )
111 {
112 if ( "value" == values.item( i ).nodeName() )
113 {
114 // <value>s have only one element, which contains actual string value
115 valueStringList.append( values.item( i ).firstChild().nodeValue() );
116 }
117 else
118 {
119 QgsDebugError( u"non <value> element ``%1'' in string list"_s.arg( values.item( i ).nodeName() ) );
120 }
121
122 ++i;
123 }
124
125 mValue = valueStringList;
126 break;
127 }
128
129 case QMetaType::Type::QFont:
130 QgsDebugError( u"no support for QVariant::Font"_s );
131 return false;
132
133 case QMetaType::Type::QPixmap:
134 QgsDebugError( u"no support for QVariant::Pixmap"_s );
135 return false;
136
137 case QMetaType::Type::QBrush:
138 QgsDebugError( u"no support for QVariant::Brush"_s );
139 return false;
140
141 case QMetaType::Type::QRect:
142 QgsDebugError( u"no support for QVariant::Rect"_s );
143 return false;
144
145 case QMetaType::Type::QSize:
146 QgsDebugError( u"no support for QVariant::Size"_s );
147 return false;
148
149 case QMetaType::Type::QColor:
150 QgsDebugError( u"no support for QVariant::Color"_s );
151 return false;
152
153 case QMetaType::Type::QPalette:
154 QgsDebugError( u"no support for QVariant::Palette"_s );
155 return false;
156
157 case QMetaType::Type::QPoint:
158 QgsDebugError( u"no support for QVariant::Point"_s );
159 return false;
160
161 case QMetaType::Type::QImage:
162 QgsDebugError( u"no support for QVariant::Image"_s );
163 return false;
164
165 case QMetaType::Type::Int:
166 mValue = QVariant( subkeyElement.text() ).toInt();
167 break;
168
169 case QMetaType::Type::UInt:
170 mValue = QVariant( subkeyElement.text() ).toUInt();
171 break;
172
173 case QMetaType::Type::Bool:
174 mValue = QVariant( subkeyElement.text() ).toBool();
175 break;
176
177 case QMetaType::Type::Double:
178 mValue = QVariant( subkeyElement.text() ).toDouble();
179 break;
180
181 case QMetaType::Type::QByteArray:
182 mValue = QVariant( subkeyElement.text() ).toByteArray();
183 break;
184
185 case QMetaType::Type::QPolygon:
186 QgsDebugError( u"no support for QVariant::Polygon"_s );
187 return false;
188
189 case QMetaType::Type::QRegion:
190 QgsDebugError( u"no support for QVariant::Region"_s );
191 return false;
192
193 case QMetaType::Type::QBitmap:
194 QgsDebugError( u"no support for QVariant::Bitmap"_s );
195 return false;
196
197 case QMetaType::Type::QCursor:
198 QgsDebugError( u"no support for QVariant::Cursor"_s );
199 return false;
200
201 case QMetaType::Type::QBitArray :
202 QgsDebugError( u"no support for QVariant::BitArray"_s );
203 return false;
204
205 case QMetaType::Type::QKeySequence :
206 QgsDebugError( u"no support for QVariant::KeySequence"_s );
207 return false;
208
209 case QMetaType::Type::QPen :
210 QgsDebugError( u"no support for QVariant::Pen"_s );
211 return false;
212
213#if 0 // Currently unsupported variant types
214 case QVariant::LongLong :
215 value_ = QVariant( subkeyElement.text() ).toLongLong();
216 break;
217
218 case QVariant::ULongLong :
219 value_ = QVariant( subkeyElement.text() ).toULongLong();
220 break;
221#endif
222 default :
223 QgsDebugError( u"unsupported value type %1 .. not properly translated to QVariant"_s.arg( typeString ) );
224 }
225
226 return true;
227
228}
229
230
231// keyElement is created by parent QgsProjectPropertyKey
232bool QgsProjectPropertyValue::writeXml( QString const &nodeName,
233 QDomElement &keyElement,
234 QDomDocument &document )
235{
236 QDomElement valueElement = document.createElement( u"properties"_s );
237
238 // remember the type so that we can rebuild it when the project is read in
239 valueElement.setAttribute( u"name"_s, nodeName );
240 valueElement.setAttribute( u"type"_s, mValue.typeName() );
241
242 // we handle string lists differently from other types in that we
243 // create a sequence of repeated elements to cover all the string list
244 // members; each value will be in a <value></value> tag.
245 // XXX Not the most elegant way to handle string lists?
246 if ( QMetaType::Type::QStringList == mValue.userType() )
247 {
248 QStringList sl = mValue.toStringList();
249
250 for ( QStringList::iterator i = sl.begin();
251 i != sl.end();
252 ++i )
253 {
254 QDomElement stringListElement = document.createElement( u"value"_s );
255 QDomText valueText = document.createTextNode( *i );
256 stringListElement.appendChild( valueText );
257
258 valueElement.appendChild( stringListElement );
259 }
260 }
261 else // we just plop the value in as plain ole text
262 {
263 QDomText valueText = document.createTextNode( mValue.toString() );
264 valueElement.appendChild( valueText );
265 }
266
267 keyElement.appendChild( valueElement );
268
269 return true;
270}
271
272
274 : mName( name )
275{}
276
281
283{
284 QgsProjectProperty *foundQgsProperty = mProperties.value( name() );
285
286 if ( !foundQgsProperty )
287 {
288 QgsDebugError( u"key has null child"_s );
289 return QVariant(); // just return an QVariant::Invalid
290 }
291
292 return foundQgsProperty->value();
293}
294
295
296void QgsProjectPropertyKey::dump( int tabs ) const
297{
298 QString tabString;
299
300 tabString.fill( '\t', tabs );
301
302 QgsDebugMsgLevel( u"%1name: %2"_s.arg( tabString, name() ), 4 );
303
304 tabs++;
305 tabString.fill( '\t', tabs );
306
307 if ( ! mProperties.isEmpty() )
308 {
309 QHashIterator < QString, QgsProjectProperty * > i( mProperties );
310 while ( i.hasNext() )
311 {
312 if ( i.next().value()->isValue() )
313 {
314 QgsProjectPropertyValue *propertyValue = static_cast<QgsProjectPropertyValue *>( i.value() );
315
316 if ( QMetaType::Type::QStringList == propertyValue->value().userType() )
317 {
318 QgsDebugMsgLevel( u"%1key: <%2> value:"_s.arg( tabString, i.key() ), 4 );
319 propertyValue->dump( tabs + 1 );
320 }
321 else
322 {
323 QgsDebugMsgLevel( u"%1key: <%2> value: %3"_s.arg( tabString, i.key(), propertyValue->value().toString() ), 4 );
324 }
325 }
326 else
327 {
328 QgsDebugMsgLevel( u"%1key: <%2> subkey: <%3>"_s
329 .arg( tabString,
330 i.key(),
331 static_cast<QgsProjectPropertyKey *>( i.value() )->name() ), 4 );
332 i.value()->dump( tabs + 1 );
333 }
334
335#if 0
336 qDebug( "<%s>", name().toUtf8().constData() );
337 if ( i.value()->isValue() )
338 {
339 qDebug( " <%s>", i.key().toUtf8().constData() );
340 }
341 i.value()->dump();
342 if ( i.value()->isValue() )
343 {
344 qDebug( " </%s>", i.key().toUtf8().constData() );
345 }
346 qDebug( "</%s>", name().toUtf8().constData() );
347#endif
348 }
349 }
350
351}
352
353
354
355bool QgsProjectPropertyKey::readXml( const QDomNode &keyNode )
356{
357 int i = 0;
358 QDomNodeList subkeys = keyNode.childNodes();
359
360 while ( i < subkeys.count() )
361 {
362 const QDomNode subkey = subkeys.item( i );
363 QString name;
364
365 if ( subkey.nodeName() == "properties"_L1 &&
366 subkey.hasAttributes() && // if we have attributes
367 subkey.isElement() && // and we're an element
368 subkey.toElement().hasAttribute( u"name"_s ) ) // and we have a "name" attribute
369 name = subkey.toElement().attribute( u"name"_s );
370 else
371 name = subkey.nodeName();
372
373 // if the current node is an element that has a "type" attribute,
374 // then we know it's a leaf node; i.e., a subkey _value_, and not
375 // a subkey
376 if ( subkey.hasAttributes() && // if we have attributes
377 subkey.isElement() && // and we're an element
378 subkey.toElement().hasAttribute( u"type"_s ) ) // and we have a "type" attribute
379 {
380 // then we're a key value
381 //
382 delete mProperties.take( name );
383 mProperties.insert( name, new QgsProjectPropertyValue );
384
385 if ( !mProperties[name]->readXml( subkey ) )
386 {
387 QgsDebugError( u"unable to parse key value %1"_s.arg( name ) );
388 }
389 }
390 else // otherwise it's a subkey, so just recurse on down the remaining keys
391 {
392 addKey( name );
393
394 if ( !mProperties[name]->readXml( subkey ) )
395 {
396 QgsDebugError( u"unable to parse subkey %1"_s.arg( name ) );
397 }
398 }
399
400 ++i;
401 }
402
403 return true;
404}
405
406
407/*
408 Property keys will always create a Dom element for itself and then
409 recursively call writeXml for any constituent properties.
410*/
411bool QgsProjectPropertyKey::writeXml( QString const &nodeName, QDomElement &element, QDomDocument &document )
412{
413 // If it's an _empty_ node (i.e., one with no properties) we need to emit
414 // an empty place holder; else create new Dom elements as necessary.
415
416 QDomElement keyElement = document.createElement( "properties" ); // Dom element for this property key
417 keyElement.toElement().setAttribute( u"name"_s, nodeName );
418
419 if ( ! mProperties.isEmpty() )
420 {
421 auto keys = mProperties.keys();
422 std::sort( keys.begin(), keys.end() );
423
424 for ( const auto &key : std::as_const( keys ) )
425 {
426 if ( !mProperties.value( key )->writeXml( key, keyElement, document ) )
427 QgsMessageLog::logMessage( tr( "Failed to save project property %1" ).arg( key ) );
428 }
429 }
430
431 element.appendChild( keyElement );
432
433 return true;
434}
435
436void QgsProjectPropertyKey::entryList( QStringList &entries ) const
437{
438 // now add any leaf nodes to the entries list
439 QHashIterator < QString, QgsProjectProperty * > i( mProperties );
440 while ( i.hasNext() )
441 {
442 // add any of the nodes that have just a single value
443 if ( i.next().value()->isLeaf() )
444 {
445 entries.append( i.key() );
446 }
447 }
448}
449
450void QgsProjectPropertyKey::subkeyList( QStringList &entries ) const
451{
452 // now add any leaf nodes to the entries list
453 QHashIterator < QString, QgsProjectProperty * > i( mProperties );
454 while ( i.hasNext() )
455 {
456 // add any of the nodes that have just a single value
457 if ( !i.next().value()->isLeaf() )
458 {
459 entries.append( i.key() );
460 }
461 }
462}
463
464
466{
467 if ( 0 == count() )
468 {
469 return true;
470 }
471 else if ( 1 == count() )
472 {
473 QHashIterator < QString, QgsProjectProperty * > i( mProperties );
474
475 if ( i.hasNext() && i.next().value()->isValue() )
476 {
477 return true;
478 }
479 }
480
481 return false;
482}
483
485{
486 mName = name;
487}
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
bool isLeaf() const override
Returns true if property is a leaf node.
QString name() const
The name of the property is used as identifier.
QgsProjectPropertyKey(const QString &name=QString())
Create a new QgsProjectPropertyKey with the specified identifier.
void dump(int tabs=0) const override
Dumps out the keys and values.
virtual void clearKeys()
Deletes any sub-nodes from the property.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
void setName(const QString &name)
The name of the property is used as identifier.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
QVariant value() const override
If this key has a value, it will be stored by its name in its properties.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
int count() const
Returns the number of sub-keys contained by this property.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
Project property value node, contains a QgsProjectPropertyKey's value.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
QVariant value() const override
Returns the node's value.
void dump(int tabs=0) const override
Dumps out the keys and values.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
virtual QVariant value() const =0
Returns the node's value.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59