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