KProperty

KProperty.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
3 Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net>
4 Copyright (C) 2004-2017 Jarosław Staniek <staniek@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22#include "KProperty.h"
23#include "KPropertyFactory.h"
24#include "KPropertyListData.h"
25#include "KPropertySet_p.h"
26#include "KProperty_p.h"
27#include "kproperty_debug.h"
28
29#include <math.h>
30
31//! @return true if @a currentValue and @a value are compatible
32static bool compatibleTypes(const QVariant& currentValue, const QVariant &value)
33{
34 if (currentValue.isNull() || value.isNull())
35 return true;
36 const QVariant::Type t = currentValue.type();
37 const QVariant::Type newt = value.type();
38 if (t == newt)
39 return true;
40 if ( (t == QVariant::Int && newt == QVariant::UInt)
41 || (t == QVariant::UInt && newt == QVariant::Int)
42 || (t == QVariant::ByteArray && newt == QVariant::String)
43 || (t == QVariant::String && newt == QVariant::ByteArray)
44 || (t == QVariant::ULongLong && newt == QVariant::LongLong)
45 || (t == QVariant::LongLong && newt == QVariant::ULongLong))
46 {
47 return true;
48 }
49 return false;
50}
51
52// ----
53
54KProperty::Private::Private(KProperty *prop)
55 : q(prop), type(KProperty::Auto), listData(nullptr), changed(false), storable(true),
56 readOnly(false), visible(true),
57 composed(nullptr), useComposedProperty(true),
58 sets(nullptr), parent(nullptr), children(nullptr), relatedProperties(nullptr)
59{
60}
61
62void KProperty::Private::setCaptionForDisplaying(const QString& captionForDisplaying)
63{
64 caption = captionForDisplaying.simplified();
65 if (caption == captionForDisplaying) {
66 caption.clear();
67 }
68 this->captionForDisplaying = captionForDisplaying;
69}
70
71KProperty::Private::~Private()
72{
73 delete listData;
74 if (children) {
75 qDeleteAll(*children);
76 delete children;
77 }
78 delete relatedProperties;
79 delete composed;
80 delete sets;
81}
82
83bool KProperty::Private::valueDiffersInternal(const QVariant &otherValue, KProperty::ValueOptions options)
84{
85 if (!compatibleTypes(value, otherValue)) {
86 kprWarning() << "INCOMPATIBLE TYPES! old=" << value << "new=" << otherValue << "in property" << q->name();
87 }
88
89 const QVariant::Type t = value.type();
90 const QVariant::Type newt = otherValue.type();
91 if ( t == QVariant::DateTime
92 || t == QVariant::Time)
93 {
94 //for date and datetime types: compare with strings, because there
95 //can be miliseconds difference
96 return value.toString() != otherValue.toString();
97 }
98 else if (t == QVariant::String || t == QVariant::ByteArray) {
99 //property is changed for string type,
100 //if one of value is empty and other isn't..
101 return (value.toString().isEmpty() != otherValue.toString().isEmpty())
102 //..or both are not empty and values differ
103 || (!value.toString().isEmpty() && !otherValue.toString().isEmpty() && value != otherValue);
104 }
105 else if (t == QVariant::Double) {
106 const double factor = pow(10.0, option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION).toDouble());
107 //kprDebug()
108 // << "factor:" << factor << "precision:" << option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP)
109 // << "double compared:" << value.toDouble() << otherValue.toDouble()
110 // << ":" << static_cast<qlonglong>(value.toDouble() * factor) << static_cast<qlonglong>(otherValue.toDouble() * factor);
111 return static_cast<qlonglong>(value.toDouble() * factor) != static_cast<qlonglong>(otherValue.toDouble() * factor);
112 } else if (t == QVariant::Invalid && newt == QVariant::Invalid) {
113 return false;
114 } else if (composed && !(options & ValueOption::IgnoreComposedProperty)) {
115 return !composed->valuesEqual(value, otherValue);
116 }
117 else {
118 return value != otherValue;
119 }
120}
121
122bool KProperty::Private::setValueInternal(const QVariant &newValue, KProperty::ValueOptions valueOptions)
123{
124 if (name.isEmpty()) {
125 kprWarning() << "COULD NOT SET value to a null property";
126 return false;
127 }
128
129 //1. Check if the value should be changed
130 if (!valueDiffersInternal(newValue, valueOptions)) {
131 return false;
132 }
133
134 //2. Then change it, and store old value if necessary
135 if (valueOptions & KProperty::ValueOption::IgnoreOld) {
136 oldValue = QVariant(); // clear old value
137 changed = false;
138 } else {
139 if (!changed) {
140 oldValue = value;
141 changed = true;
142 }
143 }
144 if (parent) {
145 parent->d->childValueChanged(q, newValue, valueOptions);
146 }
147
148 QVariant prevValue;
149 if (composed && useComposedProperty) {
150 prevValue = value; //???
151 composed->setChildValueChangedEnabled(false);
152 composed->setValue(
153 q, newValue, valueOptions | ValueOption::IgnoreComposedProperty // avoid infinite recursion
154 );
155 composed->setChildValueChangedEnabled(true);
156 }
157 else {
158 prevValue = value;
159 }
160
161 value = newValue;
162
163 if (!parent) { // emit only if parent has not done it
164 emitPropertyChanged(); // called as last step in this method!
165 }
166 return true;
167}
168
169void KProperty::Private::addChild(KProperty *prop)
170{
171 if (!prop) {
172 return;
173 }
174
175 if (!children || std::find(children->begin(), children->end(), prop) == children->end()) { // not in our list
176 if (!children) {
177 children = new QList<KProperty*>();
178 }
179 children->append(prop);
180 prop->d->parent = q;
181 } else {
182 kprWarning() << "property" << name
183 << ": child property" << prop->name() << "already added";
184 return;
185 }
186}
187
188void KProperty::Private::addSet(KPropertySet *newSet)
189{
190 if (!newSet) {
191 return;
192 }
193
194 if (!set) {//simple case
195 set = newSet;
196 return;
197 }
198 if (set == newSet || (sets && sets->contains(newSet))) {
199 return;
200 }
201 if (!sets) {
203 }
204 sets->append(QPointer<KPropertySet>(newSet));
205}
206
207void KProperty::Private::addRelatedProperty(KProperty *property)
208{
209 if (!relatedProperties)
210 relatedProperties = new QList<KProperty*>();
211
212 if (!relatedProperties->contains(property)) {
213 relatedProperties->append(property);
214 }
215}
216
217void KProperty::Private::emitPropertyChanged()
218{
219 QList< QPointer<KPropertySet> > *realSets = nullptr;
220 if (sets) {
221 realSets = sets;
222 }
223 else if (parent) {
224 realSets = parent->d->sets;
225 }
226 if (realSets) {
227 foreach (QPointer<KPropertySet> s, *realSets) {
228 if (!s.isNull()) { //may be destroyed in the meantime
229 emit s->propertyChangedInternal(*s, *q);
230 emit s->propertyChanged(*s, *q);
231 }
232 }
233 }
234 else {
236 if (set) {
237 realSet = set;
238 }
239 else if (parent) {
240 realSet = parent->d->set;
241 }
242 if (!realSet.isNull()) {
243 //if the slot connect with that signal may call set->clear() - that's
244 //the case e.g. at kexi/plugins/{macros|scripting}/* - this KProperty
245 //may got destroyed ( see KPropertySet::removeProperty(KProperty*) ) while we are
246 //still on it. So, if we try to access ourself/this once the signal
247 //got emitted we may end in a very hard to reproduce crash. So, the
248 //emit should happen as last step in this method!
249 emit realSet->propertyChangedInternal(*realSet, *q);
250 emit realSet->propertyChanged(*realSet, *q);
251 }
252 }
253}
254
255void KProperty::Private::childValueChanged(KProperty *child, const QVariant &value,
256 KProperty::ValueOptions valueOptions)
257{
258 if (!composed) {
259 return;
260 }
261 composed->childValueChangedInternal(
262 child, value,
263 valueOptions | KProperty::ValueOption::IgnoreComposedProperty // avoid infinite recursion
264 );
265}
266
267/////////////////////////////////////////////////////////////////
268
269KProperty::KProperty(const QByteArray &name, const QVariant &value,
270 const QString &caption, const QString &description,
271 int type, KProperty* parent)
272 : d(new KProperty::Private(this))
273{
274 d->name = name;
275 d->setCaptionForDisplaying(caption);
276 d->description = description;
277
278 if (type == int(Auto)) {
279 type = value.type();
280 }
281 setType(type);
282
283 if (parent)
284 parent->d->addChild(this);
286}
287
289 const QVariant &value, const QString &caption, const QString &description,
290 int type, KProperty* parent)
291 : d(new KProperty::Private(this))
292{
293 d->name = name;
294 d->setCaptionForDisplaying(caption);
295 d->description = description;
296 d->listData = listData;
297 if (type == int(Auto)) {
298 type = value.type();
299 }
300 setType(type);
301
302 if (parent)
303 parent->d->addChild(this);
305}
306
308 : d(new KProperty::Private(this))
309{
310}
311
313 : d(new KProperty::Private(this))
314{
315 *this = prop;
316}
317
318KProperty::~KProperty()
319{
320 delete d;
321}
322
325{
326 return d->name;
327}
328
329void
331{
332 d->name = name;
333}
334
337{
338 return d->caption.isEmpty() ? d->captionForDisplaying : d->caption;
339}
340
343{
344 return d->captionForDisplaying;
345}
346
347void
349{
350 d->setCaptionForDisplaying(caption);
351}
352
355{
356 return d->description;
357}
358
359void
361{
362 d->description = desc;
363}
364
365int
367{
368 return d->type;
369}
370
371void
373{
374 if (d->type != type) {
375 d->type = type;
376 delete d->composed;
377 d->composed = KPropertyFactoryManager::self()->createComposedProperty(this);
378 }
379}
380
383{
384 return d->iconName;
385}
386
387void
389{
390 d->iconName = name;
391}
392
395{
396 return d->value;
397}
398
401{
402 return d->oldValue;
403}
404
405bool KProperty::setValue(const QVariant &value, ValueOptions options)
406{
407 return d->setValueInternal(value, options);
408}
409
410void KProperty::setValue(const QVariant &value, bool doNotUseThisOverload, bool doNotUseThisOverload2)
411{
412 Q_UNUSED(value);
413 Q_UNUSED(doNotUseThisOverload);
414 Q_UNUSED(doNotUseThisOverload2);
415}
416
417bool KProperty::valueEqualsTo(const QVariant &value, ValueOptions valueOptions) const
418{
419 return !d->valueDiffersInternal(value, valueOptions);
420}
421
422void
424{
425 if (!d->changed) {
426 return;
427 }
428 d->changed = false;
429 bool cleared = false;
430 if (d->set) {
431 KPropertySetPrivate::d(d->set)->informAboutClearing(&cleared); //inform me about possibly clearing the property sets
432 }
434 if (cleared)
435 return; //property set has been cleared: no further actions make sense as 'this' is dead
436
437 // maybe parent prop is also unchanged now
438 if (d->parent && d->parent->value() == d->parent->oldValue())
439 d->parent->d->changed = false;
440
441 if (d->sets) {
442 foreach (QPointer<KPropertySet> set, *d->sets) {
443 if (!set.isNull()) //may be destroyed in the meantime
444 emit set->propertyReset(*set, *this);
445 }
446 } else if (d->set) {
447 emit d->set->propertyReset(*d->set, *this);
448 }
449}
450
452{
453 return d->listData;
454}
455
456void
458{
459 if (list == d->listData)
460 return;
461 delete d->listData;
462 d->listData = list;
463}
464
465void
467{
468 KPropertyListData* list = new KPropertyListData(keys, names);
469 setListData(list);
470}
471
472////////////////////////////////////////////////////////////////
473
474bool
476{
477 return d->name.isEmpty();
478}
479
480bool
482{
483 if (d->changed) {
484 return true;
485 }
486 if (d->children) {
487 for (const KProperty* p : *d->children) {
488 if (p->isModified()) {
489 return true;
490 }
491 }
492 }
493 return false;
494}
495
496void
498{
499 d->changed = false;
500 if (d->children) {
501 for (KProperty* p : *d->children) {
503 }
504 }
505}
506
507bool
509{
510 return d->readOnly;
511}
512
513void
515{
516 d->readOnly = readOnly;
517}
518
519bool
521{
522 return d->visible;
523}
524
525void
527{
528 d->visible = visible;
529}
530
532{
533 return d->valueSyncPolicy;
534}
535
536void
538{
539 d->valueSyncPolicy = policy;
540}
541
542bool
544{
545 return d->storable;
546}
547
548void
550{
551 d->storable = storable;
552}
553
554void
555KProperty::setOption(const char* name, const QVariant& val)
556{
557 if (val.isNull()) {
558 d->options.remove(name);
559 } else {
560 d->options[name] = val;
561 }
562}
563
564QVariant KProperty::option(const char* name, const QVariant& defaultValue) const
565{
566 return d->option(name, defaultValue);
567}
568
569bool
571{
572 return !d->options.isEmpty() || (d->parent && d->parent->hasOptions());
573}
574
575/////////////////////////////////////////////////////////////////
576
579{
580 setValue(val);
581 return *this;
582}
583
586{
587 if (&property == this)
588 return *this;
589
590 delete d->listData;
591 d->listData = nullptr;
592 delete d->children;
593 d->children = nullptr;
594 delete d->relatedProperties;
595 d->relatedProperties = nullptr;
596 delete d->composed;
597 d->composed = nullptr;
598
599 d->name = property.d->name;
600 d->setCaptionForDisplaying(property.captionForDisplaying());
601 d->description = property.d->description;
602 d->type = property.d->type;
603
604 d->iconName = property.d->iconName;
605 d->valueSyncPolicy = property.d->valueSyncPolicy;
606 d->visible = property.d->visible;
607 d->storable = property.d->storable;
608 d->readOnly = property.d->readOnly;
609 d->options = property.d->options;
610
611 if (property.d->listData) {
612 d->listData = new KPropertyListData(*property.d->listData);
613 }
614 if (property.d->composed) {
615 delete d->composed;
616 d->composed = KPropertyFactoryManager::self()->createComposedProperty(this);
617 // updates all children value, using KComposedPropertyInterface
618 setValue(property.value());
619 } else {
620 d->value = property.d->value;
621 if (property.d->children) {
622 // no KComposedPropertyInterface (should never happen), simply copy all children
623 d->children = new QList<KProperty*>();
624 QList<KProperty*>::ConstIterator endIt = property.d->children->constEnd();
625 for (QList<KProperty*>::ConstIterator it = property.d->children->constBegin(); it != endIt; ++it) {
626 KProperty *child = new KProperty(*(*it));
627 d->addChild(child);
628 }
629 }
630 }
631
632 if (property.d->relatedProperties) {
633 d->relatedProperties = new QList<KProperty*>(*(property.d->relatedProperties));
634 }
635
636 // update these later because they may have been changed when creating children
637 d->oldValue = property.d->oldValue;
638 d->changed = property.d->changed;
639 return *this;
640}
641
642bool
644{
645 return ((d->name == prop.d->name) && (value() == prop.value()));
646}
647
648bool KProperty::operator!=(const KProperty &prop) const
649{
650 return !operator==(prop);
651}
652
653/////////////////////////////////////////////////////////////////
654
657{
658 return d->children;
659}
660
663{
664 QList<KProperty*>::ConstIterator endIt = d->children->constEnd();
665 for (QList<KProperty*>::ConstIterator it = d->children->constBegin(); it != endIt; ++it) {
666 if ((*it)->name() == name)
667 return *it;
668 }
669 return nullptr;
670}
671
674{
675 return d->parent;
676}
677
679{
680 return d->composed;
681}
682
683void
685{
686 if (d->composed == prop)
687 return;
688 delete d->composed;
689 d->composed = prop;
690}
691
692#if 0
693int Property::sortingKey() const
694{
695 return d->sortingKey;
696}
697
698void Property::setSortingKey(int key)
699{
700 d->sortingKey = key;
701}
702#endif
703
704/////////////////////////////////////////////////////////////////
705
706KPROPERTYCORE_EXPORT QDebug operator<<(QDebug dbg, const KProperty &p)
707{
708 dbg.nospace() << "KProperty("
709 << "NAME=" << p.name();
710 if (!p.captionForDisplaying().isEmpty()) {
711 dbg.nospace() << " CAPTION_FOR_DISPLAYING=" << p.captionForDisplaying();
712 if (p.captionForDisplaying() != p.caption()) {
713 dbg.nospace() << " CAPTION=" << p.caption();
714 }
715 }
716 if (!p.description().isEmpty()) {
717 dbg.nospace() << " DESC=" << p.description();
718 }
719 dbg.nospace() << " TYPE=" << p.type();
720 if (p.value().isValid()) {
721 dbg.nospace() << " VALUE=" << p.value();
722 }
723 else {
724 dbg.nospace() << " VALUE=<INVALID>";
725 }
726 if (p.oldValue().isValid()) {
727 dbg.nospace() << " OLDVALUE=" << p.oldValue();
728 }
729 if (p.isModified()) {
730 dbg.nospace() << " MODIFIED";
731 }
732 if (!p.isVisible()) {
733 dbg.nospace() << " HIDDEN";
734 }
735
736//! @todo children...
737
738 if (p.hasOptions()) {
739 dbg.nospace() << " OPTIONS(" << p.d->options.count() << "): [";
740 QList<QByteArray> optionKeys( p.d->options.keys() );
741 std::sort(optionKeys.begin(), optionKeys.end());
742 bool first = true;
743 foreach (const QByteArray& key, optionKeys) {
744 if (first) {
745 first = false;
746 }
747 else {
748 dbg.space() << ",";
749 }
750 dbg.nospace() << key << ":" << p.option(key.constData());
751 }
752 dbg.nospace() << "]";
753 }
754
755 dbg.nospace() << ")";
756 return dbg.space();
757}
An interface for for composed property handlers.
A data container for properties of list type.
Set of properties.
The base class representing a single property.
Definition KProperty.h:96
QVariant option(const char *name, const QVariant &defaultValue=QVariant()) const
Returns value of given option Option is set if returned value is not null. If there is no option for ...
void setName(const QByteArray &name)
Sets name of the property.
KProperty()
Constructs a null property.
bool operator==(const KProperty &prop) const
void setVisible(bool visible)
int type() const
KProperty & operator=(const QVariant &val)
bool isVisible() const
KProperty * child(const QByteArray &name)
ValueSyncPolicy
Synchronization policy for property values.
Definition KProperty.h:355
QVariant value() const
bool isReadOnly() const
KComposedPropertyInterface * composedProperty() const
void setDescription(const QString &description)
bool hasOptions() const
Returns true if at least one option is specified for this property If there are no options defined tr...
void setValueSyncPolicy(ValueSyncPolicy policy)
Sets synchronization policy for property values of this property See ValueSyncPolicy for details.
QString description() const
QString captionForDisplaying() const
void resetValue()
void setIconName(const QString &name)
bool isModified() const
Return true if value of this property or value of any child property is modified.
bool setValue(const QVariant &value, ValueOptions options=ValueOptions())
Sets value of the property.
void setStorable(bool storable)
QByteArray name() const
KPropertyListData * listData() const
void setType(int type)
@ IgnoreOld
Do not remember the old value before setting a new one.
@ IgnoreComposedProperty
Do not use composed property when comparing values.
void setReadOnly(bool readOnly)
void setListData(KPropertyListData *list)
const QList< KProperty * > * children() const
bool operator!=(const KProperty &prop) const
ValueSyncPolicy valueSyncPolicy() const
void setOption(const char *name, const QVariant &val)
void setCaption(const QString &caption)
QString caption() const
QString iconName() const
void setComposedProperty(KComposedPropertyInterface *prop)
QVariant oldValue() const
bool valueEqualsTo(const QVariant &value, ValueOptions valueOptions=ValueOptions()) const
bool isNull() const
void clearModifiedFlag()
Clears the "modified" flag for this property and all its child properties.
KProperty * parent() const
bool isStorable() const
Type type(const QSqlDatabase &db)
QString name(StandardAction id)
const char * constData() const const
QDebug & nospace()
QDebug & space()
iterator begin()
iterator end()
bool isNull() const const
bool isEmpty() const const
QString simplified() const const
Type type() const const
bool isNull() const const
bool isValid() const const
void setValue(QVariant &&value)
double toDouble(bool *ok) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:11:18 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.