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