Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_meta_data_value.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007, 2010 Cyrille Berger <cberger@cberger.net>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6
8#include <QPoint>
9#include <QRegularExpression>
10#include <QTime>
11#include <QVariant>
12
13#include <klocalizedstring.h>
14
15#include <kis_debug.h>
16
17using namespace KisMetaData;
18
19struct Q_DECL_HIDDEN Value::Private {
20 Private() : type(Invalid) {}
21 union {
22 QVariant* variant;
24 QMap<QString, Value>* structure;
27 ValueType type;
28 QMap<QString, Value> propertyQualifiers;
29};
30
31Value::Value() : d(new Private)
32{
33 d->type = Invalid;
34}
35
36
37Value::Value(const QVariant& variant) : d(new Private)
38{
39 d->type = Value::Variant;
40 d->value.variant = new QVariant(variant);
41}
42
43Value::Value(const QList<Value>& array, ValueType type) : d(new Private)
44{
46 d->value.array = new QList<Value>(array);
47 d->type = type; // TODO: I am hesitating about LangArray to keep them as array or convert them to maps
48}
49
50Value::Value(const QMap<QString, Value>& structure) : d(new Private)
51{
52 d->type = Structure;
53 d->value.structure = new QMap<QString, Value>(structure);
54}
55
56Value::Value(const KisMetaData::Rational& signedRational) : d(new Private)
57{
58 d->type = Value::Rational;
59 d->value.rational = new KisMetaData::Rational(signedRational);
60}
61
62
63Value::Value(const Value& v) : d(new Private)
64{
65 d->type = Invalid;
66 *this = v;
67}
68
70{
71 d->type = v.d->type;
72 d->propertyQualifiers = v.d->propertyQualifiers;
73 switch (d->type) {
74 case Invalid:
75 break;
76 case Variant:
77 d->value.variant = new QVariant(*v.d->value.variant);
78 break;
79 case OrderedArray:
80 case UnorderedArray:
82 case LangArray:
83 d->value.array = new QList<Value>(*v.d->value.array);
84 break;
85 case Structure:
86 d->value.structure = new QMap<QString, Value>(*v.d->value.structure);
87 break;
88 case Rational:
89 d->value.rational = new KisMetaData::Rational(*v.d->value.rational);
90 }
91 return *this;
92}
93
94
96{
97 delete d;
98}
99
100void Value::addPropertyQualifier(const QString& _name, const Value& _value)
101{
102 d->propertyQualifiers[_name] = _value;
103}
104
105const QMap<QString, Value>& Value::propertyQualifiers() const
106{
107 return d->propertyQualifiers;
108}
109
111{
112 return d->type;
113}
114
115double Value::asDouble() const
116{
117 switch (type()) {
118 case Variant:
119 return d->value.variant->toDouble(0);
120 case Rational:
121 return d->value.rational->numerator / (double)d->value.rational->denominator;
122 default:
123 return 0.0;
124 }
125 return 0.0;
126}
127
129{
130 switch (type()) {
131 case Variant:
132 return d->value.variant->toInt(0);
133 case Rational:
134 return d->value.rational->numerator / d->value.rational->denominator;
135 default:
136 return 0;
137 }
138 return 0;
139}
140
141QVariant Value::asVariant() const
142{
143 switch (type()) {
144 case Variant:
145 return *d->value.variant;
146 case Rational:
147 return QVariant(QString("%1 / %2").arg(d->value.rational->numerator).arg(d->value.rational->denominator));
148 default: break;
149 }
150 return QVariant();
151}
152
153bool Value::setVariant(const QVariant& variant)
154{
155 switch (type()) {
157 *this = KisMetaData::Value(variant);
158 return true;
159 case Rational: {
160 QRegularExpression rx("([^\\/]*)\\/([^\\/]*)");
161 // TODO: erm... did someone forgot to write actual code here?
162
163 // for now just safe assert and return a failure
164 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "Rational metadata values are not implemented!");
165 return false;
166 }
168 if (d->value.variant->type() == variant.type()) {
169 *d->value.variant = variant;
170 return true;
171 }
172 }
173 return true;
174 default:
175 break;
176 }
177 return false;
178}
179
180bool Value::setStructureVariant(const QString& fieldNAme, const QVariant& variant)
181{
182 if (type() == Structure) {
183 return (*d->value.structure)[fieldNAme].setVariant(variant);
184 }
185 return false;
186}
187
188bool Value::setArrayVariant(int index, const QVariant& variant)
189{
190 if (isArray()) {
191 for (int i = d->value.array->size(); i <= index; ++i) {
192 d->value.array->append(Value());
193 }
194 (*d->value.array)[index].setVariant(variant);
195 }
196 return false;
197}
198
200{
201 if (d->type == Rational) {
202 return *d->value.rational;
203 }
204 return KisMetaData::Rational();
205}
206
208{
209 if (isArray()) {
210 return *d->value.array;
211 }
212 return QList<Value>();
213}
214
215
216bool Value::isArray() const
217{
218 return type() == OrderedArray || type() == UnorderedArray || type() == AlternativeArray;
219}
220
221QMap<QString, KisMetaData::Value> Value::asStructure() const
222{
223 if (type() == Structure) {
224 return *d->value.structure;
225 }
226 return QMap<QString, KisMetaData::Value>();
227}
228
229QDebug operator<<(QDebug debug, const Value &v)
230{
231 switch (v.type()) {
232 case Value::Invalid:
233 debug.nospace() << "invalid value";
234 break;
235 case Value::Variant:
236 debug.nospace() << "Variant: " << v.asVariant();
237 break;
241 case Value::LangArray:
242 debug.nospace() << "Array: " << v.asArray();
243 break;
244 case Value::Structure:
245 debug.nospace() << "Structure: " << v.asStructure();
246 break;
247 case Value::Rational:
248 debug.nospace() << "Rational: " << v.asRational().numerator << " / " << v.asRational().denominator;
249 break;
250 }
251 return debug.space();
252}
253
254bool Value::operator==(const Value& rhs) const
255{
256 if (d->type != rhs.d->type) return false;
257 switch (d->type) {
258 case Value::Invalid:
259 return true;
260 case Value::Variant:
261 return asVariant() == rhs.asVariant();
265 case Value::LangArray:
266 return asArray() == rhs.asArray();
267 case Value::Structure:
268 return asStructure() == rhs.asStructure();
269 case Value::Rational:
270 return asRational() == rhs.asRational();
271 }
272 return false;
273}
274
276{
277 switch (d->type) {
278 case Value::Invalid:
279 Q_ASSERT(v.type() == Value::Invalid);
280 break;
281 case Value::Variant:
282 Q_ASSERT(v.type() == Value::Variant);
283 {
284 QVariant v1 = *d->value.variant;
285 QVariant v2 = *v.d->value.variant;
286 Q_ASSERT(v2.canConvert(v1.type()));
287 switch (v1.type()) {
288 default:
289 warnMetaData << "KisMetaData: Merging metadata of type" << v1.type() << "is unsupported!";
290 break;
291 case QMetaType::QDate:
292 *d->value.variant = qMax(v1.toDate(), v2.toDate());
293 break;
294 case QMetaType::QDateTime:
295 *d->value.variant = qMax(v1.toDate(), v2.toDate());
296 break;
297 case QMetaType::Double:
298 *d->value.variant = v1.toDouble() + v2.toDouble();
299 break;
300 case QMetaType::Int:
301 *d->value.variant = v1.toInt() + v2.toInt();
302 break;
303 case QMetaType::QVariantList:
304 *d->value.variant = v1.toList() + v2.toList();
305 break;
306 case QMetaType::LongLong:
307 *d->value.variant = v1.toLongLong() + v2.toLongLong();
308 break;
309 case QMetaType::QPoint:
310 *d->value.variant = v1.toPoint() + v2.toPoint();
311 break;
312 case QMetaType::QPointF:
313 *d->value.variant = v1.toPointF() + v2.toPointF();
314 break;
315 case QMetaType::QString:
316 *d->value.variant = QVariant(v1.toString() + v2.toString());
317 break;
318 case QMetaType::QStringList:
319 *d->value.variant = v1.toStringList() + v2.toStringList();
320 break;
321 case QMetaType::QTime: {
322 QTime t1 = v1.toTime();
323 QTime t2 = v2.toTime();
324 int h = t1.hour() + t2.hour();
325 int m = t1.minute() + t2.minute();
326 int s = t1.second() + t2.second();
327 int ms = t1.msec() + t2.msec();
328 if (ms > 999) {
329 ms -= 999; s++;
330 }
331 if (s > 60) {
332 s -= 60; m++;
333 }
334 if (m > 60) {
335 m -= 60; h++;
336 }
337 if (h > 24) {
338 h -= 24;
339 }
340 *d->value.variant = QTime(h, m, s, ms);
341 }
342 break;
343 case QMetaType::UInt:
344 *d->value.variant = v1.toUInt() + v2.toUInt();
345 break;
346 case QMetaType::ULongLong:
347 *d->value.variant = v1.toULongLong() + v2.toULongLong();
348 break;
349 }
350
351 }
352 break;
356 if (v.isArray()) {
357 *(d->value.array) += *(v.d->value.array);
358 } else {
359 d->value.array->append(v);
360 }
361 }
362 break;
363 case Value::LangArray: {
364 Q_ASSERT(v.type() == Value::LangArray);
365 }
366 break;
367 case Value::Structure: {
368 Q_ASSERT(v.type() == Value::Structure);
369 break;
370 }
371 case Value::Rational: {
372 Q_ASSERT(v.type() == Value::Rational);
373 d->value.rational->numerator =
374 (d->value.rational->numerator
375 * v.d->value.rational->denominator)
376 + (v.d->value.rational->numerator
377 * d->value.rational->denominator);
378 d->value.rational->denominator *= v.d->value.rational->denominator;
379 break;
380 }
381 }
382 return *this;
383}
384
385QMap<QString, KisMetaData::Value> Value::asLangArray() const
386{
387 Q_ASSERT(d->type == LangArray);
388 QMap<QString, KisMetaData::Value> langArray;
389 Q_FOREACH (const KisMetaData::Value& val, *d->value.array) {
390 Q_ASSERT(val.d->propertyQualifiers.contains("xml:lang")); // TODO probably worth to have an assert for this in the constructor as well
391 KisMetaData::Value valKeyVal = val.d->propertyQualifiers.value("xml:lang");
392 Q_ASSERT(valKeyVal.type() == Variant);
393 QVariant valKeyVar = valKeyVal.asVariant();
394 Q_ASSERT(valKeyVar.type() == QMetaType::QString);
395 langArray[valKeyVar.toString()] = val;
396 }
397 return langArray;
398}
399
400QString Value::toString() const
401{
402 switch (type()) {
403 case Value::Invalid:
404 return i18n("Invalid value.");
405 case Value::Variant:
406 return d->value.variant->toString();
407 break;
411 case Value::LangArray: {
412 QString r = QString("[%1]{ ").arg(d->value.array->size());
413 for (int i = 0; i < d->value.array->size(); ++i) {
414 const Value& val = d->value.array->at(i);
415 r += val.toString();
416 if (i != d->value.array->size() - 1) {
417 r += ',';
418 }
419 r += ' ';
420 }
421 r += '}';
422 return r;
423 }
424 case Value::Structure: {
425 QString r = "{ ";
426 QList<QString> fields = d->value.structure->keys();
427 for (int i = 0; i < fields.count(); ++i) {
428 const QString& field = fields[i];
429 const Value& val = d->value.structure->value(field);
430 r += field + " => " + val.toString();
431 if (i != d->value.array->size() - 1) {
432 r += ',';
433 }
434 r += ' ';
435 }
436 r += '}';
437 return r;
438 }
439 break;
440 case Value::Rational:
441 return QString("%1 / %2").arg(d->value.rational->numerator).arg(d->value.rational->denominator);
442 }
443 return i18n("Invalid value.");
444}
float value(const T *src, size_t ch)
qreal v
QDebug KRITACOMMAND_EXPORT operator<<(QDebug dbg, const KisCumulativeUndoData &data)
const QMap< QString, Value > & propertyQualifiers() const
QList< KisMetaData::Value > asArray() const
bool setStructureVariant(const QString &fieldNAme, const QVariant &variant)
QMap< QString, KisMetaData::Value > asStructure() const
bool operator==(const Value &) const
QVariant asVariant() const
Value & operator=(const Value &v)
bool setVariant(const QVariant &variant)
Value & operator+=(const Value &)
ValueType
Define the possible value type.
void addPropertyQualifier(const QString &_name, const Value &)
KisMetaData::Rational asRational() const
ValueType type() const
bool setArrayVariant(int index, const QVariant &variant)
QMap< QString, KisMetaData::Value > asLangArray() const
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define warnMetaData
Definition kis_debug.h:103
KisMetaData::Rational * rational
QMap< QString, Value > propertyQualifiers
QVariant * variant
QList< Value > * array
ValueType type
QMap< QString, Value > * structure