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