File indexing completed on 2024-03-24 16:11:35

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 "KPropertySet.h"
0023 #include "KPropertySet_p.h"
0024 #include "KProperty_p.h"
0025 
0026 #include <QByteArray>
0027 
0028 KPropertySetPrivate::KPropertySetPrivate(KPropertySet *set, bool isOwnProperty)
0029     : q(set), m_ownProperty(isOwnProperty)
0030 {
0031     m_groupCaptions.insert("common", QObject::tr("General", "General properties"));
0032 }
0033 
0034 KPropertySetPrivate::~KPropertySetPrivate()
0035 {
0036 }
0037 
0038 void KPropertySetPrivate::addProperty(KProperty *property, const QByteArray &group/*, bool updateSortingKey*/)
0039 {
0040     if (!property) {
0041         kprWarning() << "property == 0";
0042         return;
0043     }
0044     if (property->isNull()) {
0045         kprWarning() << "COULD NOT ADD NULL PROPERTY";
0046         return;
0047     }
0048     const QByteArray realGroup(group.isEmpty() ? "common" : group);
0049     KProperty *p = this->property(property->name());
0050     if (p) {
0051         addRelatedProperty(p, property);
0052     }
0053     else {
0054         m_list.append(property);
0055         m_hash.insert(property->name().toLower(), property);
0056         if (property->isVisible()) {
0057             m_visiblePropertiesCount++;
0058         }
0059         addToGroup(realGroup, property);
0060     }
0061 
0062     property->d->addSet(q);
0063 #if 0
0064     if (updateSortingKey)
0065         property->setSortingKey(count());
0066 #endif
0067 }
0068 
0069 void KPropertySetPrivate::removeProperty(KProperty *property)
0070 {
0071     if (!property)
0072         return;
0073 
0074     if (!m_list.removeOne(property)) {
0075         kprDebug() << "The property set does not contain property" << property;
0076         return;
0077     }
0078     KProperty *p = m_hash.take(property->name().toLower());
0079     if (p) {
0080         removeFromGroup(p);
0081         if (p->isVisible()) {
0082             m_visiblePropertiesCount--;
0083         }
0084         if (m_ownProperty) {
0085             emit q->aboutToDeleteProperty(*q, *p);
0086             delete p;
0087         }
0088     }
0089 }
0090 
0091 void KPropertySetPrivate::clear()
0092 {
0093     if (m_informAboutClearing) {
0094         *m_informAboutClearing = true;
0095     }
0096     m_informAboutClearing = nullptr;
0097     emit q->aboutToBeCleared();
0098     m_visiblePropertiesCount = 0;
0099     qDeleteAll(m_propertiesOfGroup);
0100     m_propertiesOfGroup.clear();
0101     m_groupNames.clear();
0102     m_groupForProperties.clear();
0103     m_groupCaptions.clear();
0104     m_groupIconNames.clear();
0105     qDeleteAll(m_list);
0106     m_list.clear();
0107     m_hash.clear();
0108 }
0109 
0110 void KPropertySetPrivate::copyAttributesFrom(const KPropertySetPrivate &other)
0111 {
0112     KPropertySet *origSet = q;
0113     *this = other;
0114     q = origSet;
0115     // do not copy too deeply
0116     m_list.clear();
0117     m_hash.clear();
0118     m_propertiesOfGroup.clear();
0119     m_groupForProperties.clear();
0120     m_visiblePropertiesCount = 0;
0121     m_informAboutClearing = nullptr;
0122 }
0123 
0124 void KPropertySetPrivate::copyPropertiesFrom(
0125     const QList<KProperty*>::ConstIterator& constBegin,
0126     const QList<KProperty*>::ConstIterator& constEnd, const KPropertySet & set)
0127 {
0128     for (QList<KProperty*>::ConstIterator it(constBegin); it!=constEnd; ++it) {
0129         KProperty *prop = new KProperty(*(*it));
0130         addProperty(prop, set.d->groupForProperty( *it )
0131 #if 0
0132                     ,
0133                     false /* don't updateSortingKey, because the key is already
0134                              set in KProperty copy ctor.*/
0135 #endif
0136                    );
0137     }
0138 }
0139 
0140 void KPropertySetPrivate::addToGroup(const QByteArray &group, KProperty *property)
0141 {
0142     if (!property || group.isEmpty()) {
0143         return;
0144     }
0145     //do not add the same property to the group twice
0146     const QByteArray groupLower(group.toLower());
0147     if (groupForProperty(property) == groupLower) {
0148         kprWarning() << "Group" << group << "already contains property" << property->name();
0149         return;
0150     }
0151     QList<QByteArray>* propertiesOfGroup = m_propertiesOfGroup.value(groupLower);
0152     if (!propertiesOfGroup) {
0153         propertiesOfGroup = new QList<QByteArray>();
0154         m_propertiesOfGroup.insert(groupLower, propertiesOfGroup);
0155         m_groupNames.append(groupLower);
0156     }
0157     propertiesOfGroup->append(property->name());
0158     addPropertyToGroup(property, groupLower);
0159 }
0160 
0161 void KPropertySetPrivate::removeFromGroup(KProperty *property)
0162 {
0163     if (!property) {
0164         return;
0165     }
0166     const QByteArray group(groupForProperty(property));
0167     if (group.isEmpty()) {
0168         return;
0169     }
0170     QList<QByteArray>* propertiesOfGroup = m_propertiesOfGroup.value(group);
0171     if (propertiesOfGroup) {
0172         propertiesOfGroup->removeAt(propertiesOfGroup->indexOf(property->name()));
0173         if (propertiesOfGroup->isEmpty()) {
0174             //remove group as well
0175             m_propertiesOfGroup.take(group);
0176             delete propertiesOfGroup;
0177             const int i = m_groupNames.indexOf(group);
0178             if (i != -1) {
0179                 m_groupNames.removeAt(i);
0180             }
0181         }
0182     }
0183     removePropertyFromGroup(property);
0184 }
0185 
0186 bool KPropertySetPrivate::hasGroups() const
0187 {
0188     return m_groupNames.count() > 1 || (m_groupNames.count() == 1 && m_groupNames.first() != "common");
0189 }
0190 
0191 void KPropertySetPrivate::informAboutClearing(bool* cleared)
0192 {
0193     Q_ASSERT(cleared);
0194     *cleared = false;
0195     m_informAboutClearing = cleared;
0196 }
0197 
0198 void KPropertySetPrivate::addRelatedProperty(KProperty *p1, KProperty *p2) const
0199 {
0200     p1->d->addRelatedProperty(p2);
0201 }
0202 
0203 int KPropertySetPrivate::indexOfProperty(const KProperty *property) const
0204 {
0205     KProperty *parentProperty = property->parent();
0206     if (parentProperty) {
0207         const QList<KProperty*>* children = parentProperty->children();
0208         Q_ASSERT(children);
0209         const int index = children->indexOf(parentProperty);
0210         Q_ASSERT(index != -1);
0211         return index;
0212     }
0213     return indexOfPropertyInGroup(property);
0214 }
0215 
0216 int KPropertySetPrivate::indexOfPropertyInGroup(const KProperty *property) const
0217 {
0218     const QByteArray group(m_groupForProperties.value(const_cast<KProperty *>(property)));
0219     QList<QByteArray>* propertiesOfGroup = m_propertiesOfGroup.value(group);
0220     if (!propertiesOfGroup) {
0221         return -1;
0222     }
0223     return propertiesOfGroup->indexOf(property->name());
0224 }
0225 
0226 QString KPropertySetPrivate::groupCaption(const QByteArray &group) const
0227 {
0228     const QString result(m_groupCaptions.value(group.toLower()));
0229     if (!result.isEmpty())
0230         return result;
0231     return QLatin1String(group);
0232 }
0233 
0234 //////////////////////////////////////////////
0235 
0236 KPropertySelector::KPropertySelector()
0237 {
0238 }
0239 
0240 KPropertySelector::~KPropertySelector()
0241 {
0242 }
0243 
0244 //////////////////////////////////////////////
0245 
0246 typedef QPair<KProperty*, QString> Iterator_PropertyAndString;
0247 
0248 static inline bool Iterator_propertyAndStringLessThan(
0249     const Iterator_PropertyAndString &n1, const Iterator_PropertyAndString &n2)
0250 {
0251     return QString::compare(n1.second, n2.second, Qt::CaseInsensitive) < 0;
0252 }
0253 
0254 //////////////////////////////////////////////
0255 
0256 class Q_DECL_HIDDEN KPropertySetIterator::Private
0257 {
0258 public:
0259     explicit Private(KPropertySetIterator *iter) : q(iter)
0260     {
0261     }
0262     Private(KPropertySetIterator *iter, const Private &other)
0263     : q(iter)
0264     {
0265         copy(other);
0266     }
0267     ~Private()
0268     {
0269         delete selector;
0270     }
0271 
0272 #define KPropertySetIteratorPrivateArgs(o) std::tie(o.set, o.iterator, o.end, o.selector, o.order, o.sorted)
0273     void copy(const Private &other) {
0274         KPropertySetIteratorPrivateArgs((*this)) = KPropertySetIteratorPrivateArgs(other);
0275     }
0276     bool operator==(const Private &other) const {
0277         return KPropertySetIteratorPrivateArgs((*this)) == KPropertySetIteratorPrivateArgs(other);
0278     }
0279 
0280     void skipNotAcceptable()
0281     {
0282         if (!selector)
0283             return;
0284         //kprDebug() << "FROM:" << *current();
0285         if (q->current() && !(*selector)( *q->current() )) {
0286             // skip first items that not are acceptable by the selector
0287             ++(*q);
0288         }
0289         //kprDebug() << "TO:" << *current();
0290     }
0291 
0292     const KPropertySet *set;
0293     QList<KProperty*>::ConstIterator iterator;
0294     QList<KProperty*>::ConstIterator end;
0295     KPropertySelector *selector;
0296     KPropertySetIterator::Order order;
0297     QList<KProperty*> sorted; //!< for sorted order
0298 
0299 private:
0300     KPropertySetIterator * const q;
0301 };
0302 
0303 KPropertySetIterator::KPropertySetIterator(const KPropertySet &set)
0304     : d(new Private(this))
0305 {
0306     d->set = &set;
0307     d->iterator = KPropertySetPrivate::d(&set)->listConstIterator();
0308     d->end = KPropertySetPrivate::d(&set)->listConstEnd();
0309     d->selector = nullptr;
0310     d->order = KPropertySetIterator::Order::Insertion;
0311 }
0312 
0313 KPropertySetIterator::KPropertySetIterator(const KPropertySet &set,
0314                                            const KPropertySelector &selector)
0315     : d(new Private(this))
0316 {
0317     d->set = &set;
0318     d->iterator = KPropertySetPrivate::d(&set)->listConstIterator();
0319     d->end = KPropertySetPrivate::d(&set)->listConstEnd();
0320     d->selector = selector.clone();
0321     d->order = KPropertySetIterator::Order::Insertion;
0322     d->skipNotAcceptable();
0323 }
0324 
0325 KPropertySetIterator::KPropertySetIterator(const KPropertySetIterator &set)
0326     : d(new Private(this, *set.d))
0327 {
0328 }
0329 
0330 KPropertySetIterator& KPropertySetIterator::operator=(const KPropertySetIterator &other)
0331 {
0332     if (this != &other) {
0333         d->copy(*other.d);
0334     }
0335     return *this;
0336 }
0337 
0338 bool KPropertySetIterator::operator==(const KPropertySetIterator &other) const
0339 {
0340     return *d == *other.d;
0341 }
0342 
0343 KPropertySetIterator::~KPropertySetIterator()
0344 {
0345     delete d;
0346 }
0347 
0348 void KPropertySetIterator::setOrder(KPropertySetIterator::Order order)
0349 {
0350     if (d->order == order)
0351         return;
0352     d->order = order;
0353     switch (d->order) {
0354     case KPropertySetIterator::Order::Alphabetical:
0355     case KPropertySetIterator::Order::AlphabeticalByName:
0356     {
0357         QList<Iterator_PropertyAndString> propertiesAndStrings;
0358         d->iterator = KPropertySetPrivate::d(d->set)->listConstIterator();
0359         d->end = KPropertySetPrivate::d(d->set)->listConstEnd();
0360         for (; d->iterator!=d->end; ++d->iterator) {
0361             KProperty *prop = *d->iterator;
0362             QString captionOrName;
0363             if (d->order == KPropertySetIterator::Order::Alphabetical) {
0364                 captionOrName = prop->caption();
0365             }
0366             if (captionOrName.isEmpty()) {
0367                 captionOrName = QLatin1String(prop->name());
0368             }
0369             propertiesAndStrings.append( qMakePair(prop, captionOrName) );
0370         }
0371         std::sort(propertiesAndStrings.begin(), propertiesAndStrings.end(),
0372                   Iterator_propertyAndStringLessThan);
0373         d->sorted.clear();
0374         foreach (const Iterator_PropertyAndString& propertyAndString, propertiesAndStrings) {
0375             d->sorted.append(propertyAndString.first);
0376         }
0377         // restart the iterator
0378         d->iterator = d->sorted.constBegin();
0379         d->end = d->sorted.constEnd();
0380         break;
0381     }
0382     default:
0383         d->sorted.clear();
0384         // restart the iterator
0385         d->iterator = KPropertySetPrivate::d(d->set)->listConstIterator();
0386         d->end = KPropertySetPrivate::d(d->set)->listConstEnd();
0387     }
0388     d->skipNotAcceptable();
0389 }
0390 
0391 KPropertySetIterator::Order KPropertySetIterator::order() const
0392 {
0393     return d->order;
0394 }
0395 
0396 KProperty* KPropertySetIterator::current() const
0397 {
0398     return d->iterator == d->end ? nullptr : *d->iterator;
0399 }
0400 
0401 void KPropertySetIterator::operator ++()
0402 {
0403     while (true) {
0404         ++d->iterator;
0405         if (!d->selector)
0406             return;
0407         // selector exists
0408         if (!current()) // end encountered
0409             return;
0410         if ((*d->selector)( *current() ))
0411             return;
0412     }
0413 }
0414 
0415 //////////////////////////////////////////////
0416 
0417 KPropertySet::KPropertySet(QObject *parent)
0418         : QObject(parent)
0419         , d(new KPropertySetPrivate(this, true))
0420 {
0421 }
0422 
0423 
0424 KPropertySet::KPropertySet(const KPropertySet &set)
0425         : QObject(nullptr /* implicit sharing the parent is dangerous */)
0426         , d(new KPropertySetPrivate(this, true))
0427 {
0428     setObjectName(set.objectName());
0429     *this = set;
0430 }
0431 
0432 KPropertySet::KPropertySet(bool propertyOwner)
0433         : QObject(nullptr)
0434         , d(new KPropertySetPrivate(this, propertyOwner))
0435 {
0436 }
0437 
0438 KPropertySet::~KPropertySet()
0439 {
0440     emit aboutToBeCleared();
0441     emit aboutToBeDeleted();
0442     clear();
0443     delete d;
0444 }
0445 
0446 /////////////////////////////////////////////////////
0447 
0448 void
0449 KPropertySet::addProperty(KProperty *property, const QByteArray &group)
0450 {
0451     d->addProperty(property, group);
0452 }
0453 
0454 void
0455 KPropertySet::removeProperty(KProperty *property)
0456 {
0457     d->removeProperty(property);
0458 }
0459 
0460 void
0461 KPropertySet::removeProperty(const QByteArray &name)
0462 {
0463     KProperty *p = d->property(name);
0464     removeProperty(p);
0465 }
0466 
0467 void
0468 KPropertySet::clear()
0469 {
0470     d->clear();
0471 }
0472 
0473 /////////////////////////////////////////////////////
0474 
0475 QByteArray KPropertySet::groupNameForProperty(const QByteArray &propertyName) const
0476 {
0477     const KProperty *property = d->property(propertyName);
0478     return property ? groupNameForProperty(*property) : QByteArray();
0479 }
0480 
0481 QByteArray KPropertySet::groupNameForProperty(const KProperty &property) const
0482 {
0483     return d->groupForProperty(&property);
0484 }
0485 
0486 QList<QByteArray> KPropertySet::groupNames() const
0487 {
0488     return d->groupNames();
0489 }
0490 
0491 QList<QByteArray> KPropertySet::propertyNamesForGroup(const QByteArray &group) const
0492 {
0493     QList<QByteArray>* propertiesOfGroup = d->propertyNamesForGroup(group);
0494     return propertiesOfGroup ? *propertiesOfGroup : QList<QByteArray>();
0495 }
0496 
0497 void KPropertySet::setGroupCaption(const QByteArray &group, const QString &caption)
0498 {
0499     d->setGroupCaption(group, caption);
0500 }
0501 
0502 QString KPropertySet::groupCaption(const QByteArray &group) const
0503 {
0504     return d->groupCaption(group);
0505 }
0506 
0507 void KPropertySet::setGroupIconName(const QByteArray &group, const QString& iconName)
0508 {
0509     d->setGroupIconName(group, iconName);
0510 }
0511 
0512 QString KPropertySet::groupIconName(const QByteArray &group) const
0513 {
0514     return d->groupIconName(group);
0515 }
0516 
0517 /////////////////////////////////////////////////////
0518 
0519 int KPropertySet::count() const
0520 {
0521     return d->count();
0522 }
0523 
0524 int KPropertySet::count(const KPropertySelector& selector) const
0525 {
0526     int result = 0;
0527     for (KPropertySetIterator it(*this, selector); it.current(); ++it, result++)
0528         ;
0529     return result;
0530 }
0531 
0532 bool
0533 KPropertySet::isEmpty() const
0534 {
0535     return d->isEmpty();
0536 }
0537 
0538 bool KPropertySet::hasVisibleProperties() const
0539 {
0540     return d->visiblePropertiesCount() > 0;
0541 }
0542 
0543 bool KPropertySet::hasProperties(const KPropertySelector& selector) const
0544 {
0545     KPropertySetIterator it(*this, selector);
0546     return it.current();
0547 }
0548 
0549 bool
0550 KPropertySet::isReadOnly() const
0551 {
0552     return d->readOnly;
0553 }
0554 
0555 void
0556 KPropertySet::setReadOnly(bool readOnly)
0557 {
0558     if (d->readOnly != readOnly) {
0559         d->readOnly = readOnly;
0560         emit readOnlyFlagChanged();
0561     }
0562 }
0563 
0564 bool
0565 KPropertySet::contains(const QByteArray &name) const
0566 {
0567     return d->property(name);
0568 }
0569 
0570 KProperty&
0571 KPropertySet::property(const QByteArray &name) const
0572 {
0573     return d->propertyOrNull(name);
0574 }
0575 
0576 void KPropertySet::changePropertyIfExists(const QByteArray &property, const QVariant &value)
0577 {
0578     if (contains(property)) {
0579         changeProperty(property, value);
0580     }
0581 }
0582 
0583 KProperty&
0584 KPropertySet::operator[](const QByteArray &name) const
0585 {
0586     return d->propertyOrNull(name);
0587 }
0588 
0589 KPropertySet&
0590 KPropertySet::operator= (const KPropertySet & set)
0591 {
0592     if (&set == this)
0593         return *this;
0594 
0595     clear();
0596     d->copyAttributesFrom(*set.d);
0597     d->copyPropertiesFrom(set.d->listConstIterator(), set.d->listConstEnd(), set);
0598     return *this;
0599 }
0600 
0601 QVariant KPropertySet::propertyValue(const QByteArray &name, const QVariant& defaultValue) const
0602 {
0603     const KProperty *p = d->property(name);
0604     return p ? p->value() : defaultValue;
0605 }
0606 
0607 void
0608 KPropertySet::changeProperty(const QByteArray &property, const QVariant &value)
0609 {
0610     KProperty *p = d->property(property);
0611     if (p)
0612         p->setValue(value);
0613 }
0614 
0615 void KPropertySet::debug() const
0616 {
0617     kprDebug() << *this;
0618 }
0619 
0620 KPROPERTYCORE_EXPORT QDebug operator<<(QDebug dbg, const KPropertySet &set)
0621 {
0622     dbg.nospace() << "KPropertySet(";
0623     if (set.isEmpty()) {
0624         dbg.space() << "<EMPTY>)";
0625         return dbg.space();
0626     }
0627     dbg.nospace() << " PROPERTIES(" << set.count() << "):\n";
0628 
0629     KPropertySetIterator it(set);
0630     it.setOrder(KPropertySetIterator::Order::AlphabeticalByName);
0631     bool first = true;
0632     for ( ; it.current(); ++it) {
0633         if (first) {
0634             first = false;
0635         }
0636         else {
0637             dbg.nospace() << "\n";
0638         }
0639         dbg.nospace() << *it.current();
0640     }
0641     dbg.nospace() << "\n)";
0642     return dbg.space();
0643 }
0644 
0645 QByteArray KPropertySet::previousSelection() const
0646 {
0647     return d->previousSelection();
0648 }
0649 
0650 void KPropertySet::setPreviousSelection(const QByteArray &prevSelection)
0651 {
0652     d->setPreviousSelection(prevSelection);
0653 }
0654 
0655 QMap<QByteArray, QVariant> KPropertySet::propertyValues() const
0656 {
0657     QMap<QByteArray, QVariant> result;
0658     for (KPropertySetIterator it(*this); it.current(); ++it) {
0659         result.insert(it.current()->name(), it.current()->value());
0660     }
0661     return result;
0662 }
0663 
0664 void KPropertySet::clearModifiedFlags()
0665 {
0666     for (KPropertySetIterator it(*this); it.current(); ++it) {
0667         it.current()->clearModifiedFlag();
0668     }
0669 }
0670 
0671 bool KPropertySet::isModified() const
0672 {
0673     for (KPropertySetIterator it(*this); it.current(); ++it) {
0674         if (it.current()->isModified()) {
0675             return true;
0676         }
0677     }
0678     return false;
0679 }