Warning, file /libraries/kproperty/src/KProperty.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 }