File indexing completed on 2024-04-14 04:36:13

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
0003    Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net>
0004    Copyright (C) 2004-2017 Jarosław Staniek <staniek@kde.org>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "KProperty.h"
0023 #include "KPropertyFactory.h"
0024 #include "KPropertyListData.h"
0025 #include "KPropertySet_p.h"
0026 #include "KProperty_p.h"
0027 #include "kproperty_debug.h"
0028 
0029 #include <math.h>
0030 
0031 //! @return true if @a currentValue and @a value are compatible
0032 static bool compatibleTypes(const QVariant& currentValue, const QVariant &value)
0033 {
0034     if (currentValue.isNull() || value.isNull())
0035         return true;
0036     const QVariant::Type t = currentValue.type();
0037     const QVariant::Type newt = value.type();
0038     if (t == newt)
0039         return true;
0040     if (   (t == QVariant::Int && newt == QVariant::UInt)
0041         || (t == QVariant::UInt && newt == QVariant::Int)
0042         || (t == QVariant::ByteArray && newt == QVariant::String)
0043         || (t == QVariant::String && newt == QVariant::ByteArray)
0044         || (t == QVariant::ULongLong && newt == QVariant::LongLong)
0045         || (t == QVariant::LongLong && newt == QVariant::ULongLong))
0046     {
0047         return true;
0048     }
0049     return false;
0050 }
0051 
0052 // ----
0053 
0054 KProperty::Private::Private(KProperty *prop)
0055         : q(prop), type(KProperty::Auto), listData(nullptr), changed(false), storable(true),
0056         readOnly(false), visible(true),
0057         composed(nullptr), useComposedProperty(true),
0058         sets(nullptr), parent(nullptr), children(nullptr), relatedProperties(nullptr)
0059 {
0060 }
0061 
0062 void KProperty::Private::setCaptionForDisplaying(const QString& captionForDisplaying)
0063 {
0064     caption = captionForDisplaying.simplified();
0065     if (caption == captionForDisplaying) {
0066         caption.clear();
0067     }
0068     this->captionForDisplaying = captionForDisplaying;
0069 }
0070 
0071 KProperty::Private::~Private()
0072 {
0073     delete listData;
0074     if (children) {
0075         qDeleteAll(*children);
0076         delete children;
0077     }
0078     delete relatedProperties;
0079     delete composed;
0080     delete sets;
0081 }
0082 
0083 bool KProperty::Private::valueDiffersInternal(const QVariant &otherValue, KProperty::ValueOptions options)
0084 {
0085     if (!compatibleTypes(value, otherValue)) {
0086         kprWarning() << "INCOMPATIBLE TYPES! old=" << value << "new=" << otherValue << "in property" << q->name();
0087     }
0088 
0089     const QVariant::Type t = value.type();
0090     const QVariant::Type newt = otherValue.type();
0091     if (   t == QVariant::DateTime
0092         || t == QVariant::Time)
0093     {
0094         //for date and datetime types: compare with strings, because there
0095         //can be miliseconds difference
0096         return value.toString() != otherValue.toString();
0097     }
0098     else if (t == QVariant::String || t == QVariant::ByteArray) {
0099         //property is changed for string type,
0100         //if one of value is empty and other isn't..
0101         return (value.toString().isEmpty() != otherValue.toString().isEmpty())
0102               //..or both are not empty and values differ
0103               || (!value.toString().isEmpty() && !otherValue.toString().isEmpty() && value != otherValue);
0104     }
0105     else if (t == QVariant::Double) {
0106         const double factor = pow(10.0, option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION).toDouble());
0107         //kprDebug()
0108         //    << "factor:" << factor << "precision:" << option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP)
0109         //    << "double compared:" << value.toDouble() << otherValue.toDouble()
0110         //    << ":" << static_cast<qlonglong>(value.toDouble() * factor) << static_cast<qlonglong>(otherValue.toDouble() * factor);
0111         return static_cast<qlonglong>(value.toDouble() * factor) != static_cast<qlonglong>(otherValue.toDouble() * factor);
0112     } else if (t == QVariant::Invalid && newt == QVariant::Invalid) {
0113         return false;
0114     } else if (composed && !(options & ValueOption::IgnoreComposedProperty)) {
0115         return !composed->valuesEqual(value, otherValue);
0116     }
0117     else {
0118         return value != otherValue;
0119     }
0120 }
0121 
0122 bool KProperty::Private::setValueInternal(const QVariant &newValue, KProperty::ValueOptions valueOptions)
0123 {
0124     if (name.isEmpty()) {
0125         kprWarning() << "COULD NOT SET value to a null property";
0126         return false;
0127     }
0128 
0129     //1. Check if the value should be changed
0130     if (!valueDiffersInternal(newValue, valueOptions)) {
0131         return false;
0132     }
0133 
0134     //2. Then change it, and store old value if necessary
0135     if (valueOptions & KProperty::ValueOption::IgnoreOld) {
0136         oldValue = QVariant(); // clear old value
0137         changed = false;
0138     } else {
0139         if (!changed) {
0140             oldValue = value;
0141             changed = true;
0142         }
0143     }
0144     if (parent) {
0145         parent->d->childValueChanged(q, newValue, valueOptions);
0146     }
0147 
0148     QVariant prevValue;
0149     if (composed && useComposedProperty) {
0150         prevValue = value; //???
0151         composed->setChildValueChangedEnabled(false);
0152         composed->setValue(
0153             q, newValue, valueOptions | ValueOption::IgnoreComposedProperty // avoid infinite recursion
0154         );
0155         composed->setChildValueChangedEnabled(true);
0156     }
0157     else {
0158         prevValue = value;
0159     }
0160 
0161     value = newValue;
0162 
0163     if (!parent) { // emit only if parent has not done it
0164         emitPropertyChanged(); // called as last step in this method!
0165     }
0166     return true;
0167 }
0168 
0169 void KProperty::Private::addChild(KProperty *prop)
0170 {
0171     if (!prop) {
0172         return;
0173     }
0174 
0175     if (!children || std::find(children->begin(), children->end(), prop) == children->end()) { // not in our list
0176         if (!children) {
0177             children = new QList<KProperty*>();
0178         }
0179         children->append(prop);
0180         prop->d->parent = q;
0181     } else {
0182         kprWarning() << "property" << name
0183                    << ": child property" << prop->name() << "already added";
0184         return;
0185     }
0186 }
0187 
0188 void KProperty::Private::addSet(KPropertySet *newSet)
0189 {
0190     if (!newSet) {
0191         return;
0192     }
0193 
0194     if (!set) {//simple case
0195         set = newSet;
0196         return;
0197     }
0198     if (set == newSet || (sets && sets->contains(newSet))) {
0199         return;
0200     }
0201     if (!sets) {
0202         sets = new QList< QPointer<KPropertySet> >;
0203     }
0204     sets->append(QPointer<KPropertySet>(newSet));
0205 }
0206 
0207 void KProperty::Private::addRelatedProperty(KProperty *property)
0208 {
0209     if (!relatedProperties)
0210         relatedProperties = new QList<KProperty*>();
0211 
0212     if (!relatedProperties->contains(property)) {
0213         relatedProperties->append(property);
0214     }
0215 }
0216 
0217 void KProperty::Private::emitPropertyChanged()
0218 {
0219     QList< QPointer<KPropertySet> > *realSets = nullptr;
0220     if (sets) {
0221         realSets = sets;
0222     }
0223     else if (parent) {
0224         realSets = parent->d->sets;
0225     }
0226     if (realSets) {
0227         foreach (QPointer<KPropertySet> s, *realSets) {
0228             if (!s.isNull()) { //may be destroyed in the meantime
0229                 emit s->propertyChangedInternal(*s, *q);
0230                 emit s->propertyChanged(*s, *q);
0231             }
0232         }
0233     }
0234     else {
0235         QPointer<KPropertySet> realSet;
0236         if (set) {
0237             realSet = set;
0238         }
0239         else if (parent) {
0240             realSet = parent->d->set;
0241         }
0242         if (!realSet.isNull()) {
0243             //if the slot connect with that signal may call set->clear() - that's
0244             //the case e.g. at kexi/plugins/{macros|scripting}/* -  this KProperty
0245             //may got destroyed ( see KPropertySet::removeProperty(KProperty*) ) while we are
0246             //still on it. So, if we try to access ourself/this once the signal
0247             //got emitted we may end in a very hard to reproduce crash. So, the
0248             //emit should happen as last step in this method!
0249             emit realSet->propertyChangedInternal(*realSet, *q);
0250             emit realSet->propertyChanged(*realSet, *q);
0251         }
0252     }
0253 }
0254 
0255 void KProperty::Private::childValueChanged(KProperty *child, const QVariant &value,
0256                                            KProperty::ValueOptions valueOptions)
0257 {
0258     if (!composed) {
0259         return;
0260     }
0261     composed->childValueChangedInternal(
0262         child, value,
0263         valueOptions | KProperty::ValueOption::IgnoreComposedProperty // avoid infinite recursion
0264     );
0265 }
0266 
0267 /////////////////////////////////////////////////////////////////
0268 
0269 KProperty::KProperty(const QByteArray &name, const QVariant &value,
0270                    const QString &caption, const QString &description,
0271                    int type, KProperty* parent)
0272         : d(new KProperty::Private(this))
0273 {
0274     d->name = name;
0275     d->setCaptionForDisplaying(caption);
0276     d->description = description;
0277 
0278     if (type == int(Auto)) {
0279         type = value.type();
0280     }
0281     setType(type);
0282 
0283     if (parent)
0284         parent->d->addChild(this);
0285     setValue(value, ValueOption::IgnoreOld);
0286 }
0287 
0288 KProperty::KProperty(const QByteArray &name, KPropertyListData* listData,
0289                    const QVariant &value, const QString &caption, const QString &description,
0290                    int type, KProperty* parent)
0291         : d(new KProperty::Private(this))
0292 {
0293     d->name = name;
0294     d->setCaptionForDisplaying(caption);
0295     d->description = description;
0296     d->listData = listData;
0297     if (type == int(Auto)) {
0298         type = value.type();
0299     }
0300     setType(type);
0301 
0302     if (parent)
0303         parent->d->addChild(this);
0304     setValue(value, KProperty::ValueOption::IgnoreOld);
0305 }
0306 
0307 KProperty::KProperty()
0308         : d(new KProperty::Private(this))
0309 {
0310 }
0311 
0312 KProperty::KProperty(const KProperty &prop)
0313         : d(new KProperty::Private(this))
0314 {
0315     *this = prop;
0316 }
0317 
0318 KProperty::~KProperty()
0319 {
0320     delete d;
0321 }
0322 
0323 QByteArray
0324 KProperty::name() const
0325 {
0326     return d->name;
0327 }
0328 
0329 void
0330 KProperty::setName(const QByteArray &name)
0331 {
0332     d->name = name;
0333 }
0334 
0335 QString
0336 KProperty::caption() const
0337 {
0338     return d->caption.isEmpty() ? d->captionForDisplaying : d->caption;
0339 }
0340 
0341 QString
0342 KProperty::captionForDisplaying() const
0343 {
0344     return d->captionForDisplaying;
0345 }
0346 
0347 void
0348 KProperty::setCaption(const QString &caption)
0349 {
0350     d->setCaptionForDisplaying(caption);
0351 }
0352 
0353 QString
0354 KProperty::description() const
0355 {
0356     return d->description;
0357 }
0358 
0359 void
0360 KProperty::setDescription(const QString &desc)
0361 {
0362     d->description = desc;
0363 }
0364 
0365 int
0366 KProperty::type() const
0367 {
0368     return d->type;
0369 }
0370 
0371 void
0372 KProperty::setType(int type)
0373 {
0374     if (d->type != type) {
0375         d->type = type;
0376         delete d->composed;
0377         d->composed = KPropertyFactoryManager::self()->createComposedProperty(this);
0378     }
0379 }
0380 
0381 QString
0382 KProperty::iconName() const
0383 {
0384     return d->iconName;
0385 }
0386 
0387 void
0388 KProperty::setIconName(const QString &name)
0389 {
0390     d->iconName = name;
0391 }
0392 
0393 QVariant
0394 KProperty::value() const
0395 {
0396     return d->value;
0397 }
0398 
0399 QVariant
0400 KProperty::oldValue() const
0401 {
0402     return d->oldValue;
0403 }
0404 
0405 bool KProperty::setValue(const QVariant &value, ValueOptions options)
0406 {
0407     return d->setValueInternal(value, options);
0408 }
0409 
0410 void KProperty::setValue(const QVariant &value, bool doNotUseThisOverload, bool doNotUseThisOverload2)
0411 {
0412     Q_UNUSED(value);
0413     Q_UNUSED(doNotUseThisOverload);
0414     Q_UNUSED(doNotUseThisOverload2);
0415 }
0416 
0417 bool KProperty::valueEqualsTo(const QVariant &value, ValueOptions valueOptions) const
0418 {
0419     return !d->valueDiffersInternal(value, valueOptions);
0420 }
0421 
0422 void
0423 KProperty::resetValue()
0424 {
0425     if (!d->changed) {
0426         return;
0427     }
0428     d->changed = false;
0429     bool cleared = false;
0430     if (d->set) {
0431         KPropertySetPrivate::d(d->set)->informAboutClearing(&cleared); //inform me about possibly clearing the property sets
0432     }
0433     setValue(oldValue(), ValueOption::IgnoreOld);
0434     if (cleared)
0435         return; //property set has been cleared: no further actions make sense as 'this' is dead
0436 
0437     // maybe parent  prop is also unchanged now
0438     if (d->parent && d->parent->value() == d->parent->oldValue())
0439         d->parent->d->changed = false;
0440 
0441     if (d->sets) {
0442         foreach (QPointer<KPropertySet> set, *d->sets) {
0443             if (!set.isNull()) //may be destroyed in the meantime
0444                 emit set->propertyReset(*set, *this);
0445         }
0446     } else if (d->set) {
0447         emit d->set->propertyReset(*d->set, *this);
0448     }
0449 }
0450 
0451 KPropertyListData* KProperty::listData() const
0452 {
0453     return d->listData;
0454 }
0455 
0456 void
0457 KProperty::setListData(KPropertyListData* list)
0458 {
0459     if (list == d->listData)
0460         return;
0461     delete d->listData;
0462     d->listData = list;
0463 }
0464 
0465 void
0466 KProperty::setListData(const QStringList &keys, const QStringList &names)
0467 {
0468     KPropertyListData* list = new KPropertyListData(keys, names);
0469     setListData(list);
0470 }
0471 
0472 ////////////////////////////////////////////////////////////////
0473 
0474 bool
0475 KProperty::isNull() const
0476 {
0477     return d->name.isEmpty();
0478 }
0479 
0480 bool
0481 KProperty::isModified() const
0482 {
0483     if (d->changed) {
0484         return true;
0485     }
0486     if (d->children) {
0487         for (const KProperty* p : *d->children) {
0488             if (p->isModified()) {
0489                 return true;
0490             }
0491         }
0492     }
0493     return false;
0494 }
0495 
0496 void
0497 KProperty::clearModifiedFlag()
0498 {
0499     d->changed = false;
0500     if (d->children) {
0501         for (KProperty* p : *d->children) {
0502             p->clearModifiedFlag();
0503         }
0504     }
0505 }
0506 
0507 bool
0508 KProperty::isReadOnly() const
0509 {
0510     return d->readOnly;
0511 }
0512 
0513 void
0514 KProperty::setReadOnly(bool readOnly)
0515 {
0516     d->readOnly = readOnly;
0517 }
0518 
0519 bool
0520 KProperty::isVisible() const
0521 {
0522     return d->visible;
0523 }
0524 
0525 void
0526 KProperty::setVisible(bool visible)
0527 {
0528     d->visible = visible;
0529 }
0530 
0531 KProperty::ValueSyncPolicy KProperty::valueSyncPolicy() const
0532 {
0533     return d->valueSyncPolicy;
0534 }
0535 
0536 void
0537 KProperty::setValueSyncPolicy(KProperty::ValueSyncPolicy policy)
0538 {
0539     d->valueSyncPolicy = policy;
0540 }
0541 
0542 bool
0543 KProperty::isStorable() const
0544 {
0545     return d->storable;
0546 }
0547 
0548 void
0549 KProperty::setStorable(bool storable)
0550 {
0551     d->storable = storable;
0552 }
0553 
0554 void
0555 KProperty::setOption(const char* name, const QVariant& val)
0556 {
0557     if (val.isNull()) {
0558         d->options.remove(name);
0559     } else {
0560         d->options[name] = val;
0561     }
0562 }
0563 
0564 QVariant KProperty::option(const char* name, const QVariant& defaultValue) const
0565 {
0566     return d->option(name, defaultValue);
0567 }
0568 
0569 bool
0570 KProperty::hasOptions() const
0571 {
0572     return !d->options.isEmpty() || (d->parent && d->parent->hasOptions());
0573 }
0574 
0575 /////////////////////////////////////////////////////////////////
0576 
0577 KProperty&
0578 KProperty::operator= (const QVariant & val)
0579 {
0580     setValue(val);
0581     return *this;
0582 }
0583 
0584 KProperty&
0585 KProperty::operator= (const KProperty & property)
0586 {
0587     if (&property == this)
0588         return *this;
0589 
0590     delete d->listData;
0591     d->listData = nullptr;
0592     delete d->children;
0593     d->children = nullptr;
0594     delete d->relatedProperties;
0595     d->relatedProperties = nullptr;
0596     delete d->composed;
0597     d->composed = nullptr;
0598 
0599     d->name = property.d->name;
0600     d->setCaptionForDisplaying(property.captionForDisplaying());
0601     d->description = property.d->description;
0602     d->type = property.d->type;
0603 
0604     d->iconName = property.d->iconName;
0605     d->valueSyncPolicy = property.d->valueSyncPolicy;
0606     d->visible = property.d->visible;
0607     d->storable = property.d->storable;
0608     d->readOnly = property.d->readOnly;
0609     d->options = property.d->options;
0610 
0611     if (property.d->listData) {
0612         d->listData = new KPropertyListData(*property.d->listData);
0613     }
0614     if (property.d->composed) {
0615         delete d->composed;
0616         d->composed = KPropertyFactoryManager::self()->createComposedProperty(this);
0617         // updates all children value, using KComposedPropertyInterface
0618         setValue(property.value());
0619     } else {
0620         d->value = property.d->value;
0621         if (property.d->children) {
0622             // no KComposedPropertyInterface (should never happen), simply copy all children
0623             d->children = new QList<KProperty*>();
0624             QList<KProperty*>::ConstIterator endIt = property.d->children->constEnd();
0625             for (QList<KProperty*>::ConstIterator it = property.d->children->constBegin(); it != endIt; ++it) {
0626                 KProperty *child = new KProperty(*(*it));
0627                 d->addChild(child);
0628             }
0629         }
0630     }
0631 
0632     if (property.d->relatedProperties) {
0633         d->relatedProperties = new QList<KProperty*>(*(property.d->relatedProperties));
0634     }
0635 
0636     // update these later because they may have been changed when creating children
0637     d->oldValue = property.d->oldValue;
0638     d->changed = property.d->changed;
0639     return *this;
0640 }
0641 
0642 bool
0643 KProperty::operator ==(const KProperty &prop) const
0644 {
0645     return ((d->name == prop.d->name) && (value() == prop.value()));
0646 }
0647 
0648 bool KProperty::operator!=(const KProperty &prop) const
0649 {
0650     return !operator==(prop);
0651 }
0652 
0653 /////////////////////////////////////////////////////////////////
0654 
0655 const QList<KProperty*>*
0656 KProperty::children() const
0657 {
0658     return d->children;
0659 }
0660 
0661 KProperty*
0662 KProperty::child(const QByteArray &name)
0663 {
0664     QList<KProperty*>::ConstIterator endIt = d->children->constEnd();
0665     for (QList<KProperty*>::ConstIterator it = d->children->constBegin(); it != endIt; ++it) {
0666         if ((*it)->name() == name)
0667             return *it;
0668     }
0669     return nullptr;
0670 }
0671 
0672 KProperty*
0673 KProperty::parent() const
0674 {
0675     return d->parent;
0676 }
0677 
0678 KComposedPropertyInterface* KProperty::composedProperty() const
0679 {
0680     return d->composed;
0681 }
0682 
0683 void
0684 KProperty::setComposedProperty(KComposedPropertyInterface *prop)
0685 {
0686     if (d->composed == prop)
0687         return;
0688     delete d->composed;
0689     d->composed = prop;
0690 }
0691 
0692 #if 0
0693 int Property::sortingKey() const
0694 {
0695     return d->sortingKey;
0696 }
0697 
0698 void Property::setSortingKey(int key)
0699 {
0700     d->sortingKey = key;
0701 }
0702 #endif
0703 
0704 /////////////////////////////////////////////////////////////////
0705 
0706 KPROPERTYCORE_EXPORT QDebug operator<<(QDebug dbg, const KProperty &p)
0707 {
0708     dbg.nospace() << "KProperty("
0709         << "NAME=" << p.name();
0710     if (!p.captionForDisplaying().isEmpty()) {
0711         dbg.nospace() << " CAPTION_FOR_DISPLAYING=" << p.captionForDisplaying();
0712         if (p.captionForDisplaying() != p.caption()) {
0713             dbg.nospace() << " CAPTION=" << p.caption();
0714         }
0715     }
0716     if (!p.description().isEmpty()) {
0717         dbg.nospace() << " DESC=" << p.description();
0718     }
0719     dbg.nospace() << " TYPE=" << p.type();
0720     if (p.value().isValid()) {
0721         dbg.nospace() << " VALUE=" << p.value();
0722     }
0723     else {
0724         dbg.nospace() << " VALUE=<INVALID>";
0725     }
0726     if (p.oldValue().isValid()) {
0727         dbg.nospace() << " OLDVALUE=" << p.oldValue();
0728     }
0729     if (p.isModified()) {
0730         dbg.nospace() << " MODIFIED";
0731     }
0732     if (!p.isVisible()) {
0733         dbg.nospace() << " HIDDEN";
0734     }
0735 
0736 //! @todo children...
0737 
0738     if (p.hasOptions()) {
0739         dbg.nospace() << " OPTIONS(" << p.d->options.count() << "): [";
0740         QList<QByteArray> optionKeys( p.d->options.keys() );
0741         std::sort(optionKeys.begin(), optionKeys.end());
0742         bool first = true;
0743         foreach (const QByteArray& key, optionKeys) {
0744             if (first) {
0745                 first = false;
0746             }
0747             else {
0748                 dbg.space() << ",";
0749             }
0750             dbg.nospace() << key << ":" << p.option(key.constData());
0751         }
0752         dbg.nospace() << "]";
0753     }
0754 
0755     dbg.nospace() << ")";
0756     return dbg.space();
0757 }