KProperty

KProperty.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2004 Cedric Pasteur <[email protected]>
3  Copyright (C) 2004 Alexander Dymo <[email protected]>
4  Copyright (C) 2004-2017 JarosÅ‚aw Staniek <[email protected]>
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
32 static 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 
54 KProperty::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 
62 void 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 
71 KProperty::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 
83 bool 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 
122 bool 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 
169 void 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 
188 void 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) {
202  sets = new QList< QPointer<KPropertySet> >;
203  }
204  sets->append(QPointer<KPropertySet>(newSet));
205 }
206 
207 void 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 
217 void 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 {
235  QPointer<KPropertySet> realSet;
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 
255 void 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 
269 KProperty::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 
318 KProperty::~KProperty()
319 {
320  delete d;
321 }
322 
325 {
326  return d->name;
327 }
328 
329 void
331 {
332  d->name = name;
333 }
334 
335 QString
337 {
338  return d->caption.isEmpty() ? d->captionForDisplaying : d->caption;
339 }
340 
341 QString
343 {
344  return d->captionForDisplaying;
345 }
346 
347 void
349 {
350  d->setCaptionForDisplaying(caption);
351 }
352 
353 QString
355 {
356  return d->description;
357 }
358 
359 void
361 {
362  d->description = desc;
363 }
364 
365 int
367 {
368  return d->type;
369 }
370 
371 void
373 {
374  if (d->type != type) {
375  d->type = type;
376  delete d->composed;
377  d->composed = KPropertyFactoryManager::self()->createComposedProperty(this);
378  }
379 }
380 
381 QString
383 {
384  return d->iconName;
385 }
386 
387 void
389 {
390  d->iconName = name;
391 }
392 
393 QVariant
395 {
396  return d->value;
397 }
398 
399 QVariant
401 {
402  return d->oldValue;
403 }
404 
405 bool KProperty::setValue(const QVariant &value, ValueOptions options)
406 {
407  return d->setValueInternal(value, options);
408 }
409 
410 void KProperty::setValue(const QVariant &value, bool doNotUseThisOverload, bool doNotUseThisOverload2)
411 {
412  Q_UNUSED(value);
413  Q_UNUSED(doNotUseThisOverload);
414  Q_UNUSED(doNotUseThisOverload2);
415 }
416 
417 bool KProperty::valueEqualsTo(const QVariant &value, ValueOptions valueOptions) const
418 {
419  return !d->valueDiffersInternal(value, valueOptions);
420 }
421 
422 void
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 
456 void
458 {
459  if (list == d->listData)
460  return;
461  delete d->listData;
462  d->listData = list;
463 }
464 
465 void
467 {
468  KPropertyListData* list = new KPropertyListData(keys, names);
469  setListData(list);
470 }
471 
472 ////////////////////////////////////////////////////////////////
473 
474 bool
476 {
477  return d->name.isEmpty();
478 }
479 
480 bool
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 
496 void
498 {
499  d->changed = false;
500  if (d->children) {
501  for (KProperty* p : *d->children) {
502  p->clearModifiedFlag();
503  }
504  }
505 }
506 
507 bool
509 {
510  return d->readOnly;
511 }
512 
513 void
515 {
516  d->readOnly = readOnly;
517 }
518 
519 bool
521 {
522  return d->visible;
523 }
524 
525 void
527 {
528  d->visible = visible;
529 }
530 
532 {
533  return d->valueSyncPolicy;
534 }
535 
536 void
538 {
539  d->valueSyncPolicy = policy;
540 }
541 
542 bool
544 {
545  return d->storable;
546 }
547 
548 void
550 {
551  d->storable = storable;
552 }
553 
554 void
555 KProperty::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 
564 QVariant KProperty::option(const char* name, const QVariant& defaultValue) const
565 {
566  return d->option(name, defaultValue);
567 }
568 
569 bool
571 {
572  return !d->options.isEmpty() || (d->parent && d->parent->hasOptions());
573 }
574 
575 /////////////////////////////////////////////////////////////////
576 
577 KProperty&
579 {
580  setValue(val);
581  return *this;
582 }
583 
584 KProperty&
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 
642 bool
644 {
645  return ((d->name == prop.d->name) && (value() == prop.value()));
646 }
647 
648 bool KProperty::operator!=(const KProperty &prop) const
649 {
650  return !operator==(prop);
651 }
652 
653 /////////////////////////////////////////////////////////////////
654 
655 const QList<KProperty*>*
657 {
658  return d->children;
659 }
660 
661 KProperty*
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 
672 KProperty*
674 {
675  return d->parent;
676 }
677 
679 {
680  return d->composed;
681 }
682 
683 void
685 {
686  if (d->composed == prop)
687  return;
688  delete d->composed;
689  d->composed = prop;
690 }
691 
692 #if 0
693 int Property::sortingKey() const
694 {
695  return d->sortingKey;
696 }
697 
698 void Property::setSortingKey(int key)
699 {
700  d->sortingKey = key;
701 }
702 #endif
703 
704 /////////////////////////////////////////////////////////////////
705 
706 KPROPERTYCORE_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 }
@ IgnoreComposedProperty
Do not use composed property when comparing values.
void append(const T &value)
QByteArray name() const
Definition: KProperty.cpp:324
bool isNull() const const
void setType(int type)
Definition: KProperty.cpp:372
bool isValid() const const
KComposedPropertyInterface * composedProperty() const
Definition: KProperty.cpp:678
KPropertyListData * listData() const
Definition: KProperty.cpp:451
bool operator==(const KProperty &prop) const
Definition: KProperty.cpp:643
@ IgnoreOld
Do not remember the old value before setting a new one.
bool operator!=(const KProperty &prop) const
Definition: KProperty.cpp:648
Type type(const QSqlDatabase &db)
void setListData(KPropertyListData *list)
Definition: KProperty.cpp:457
QDebug & nospace()
void clear()
void clearModifiedFlag()
Clears the "modified" flag for this property and all its child properties.
Definition: KProperty.cpp:497
QDebug & space()
QDataStream & operator<<(QDataStream &out, const KDateTime &dateTime)
bool isStorable() const
Definition: KProperty.cpp:543
bool isReadOnly() const
Definition: KProperty.cpp:508
void setDescription(const QString &description)
Definition: KProperty.cpp:360
QString caption()
void setValue(const T &value)
QString simplified() const const
KProperty * parent() const
Definition: KProperty.cpp:673
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 ...
Definition: KProperty.cpp:564
double toDouble(bool *ok) const const
void setIconName(const QString &name)
Definition: KProperty.cpp:388
The base class representing a single property.
Definition: KProperty.h:95
Set of properties.
Definition: KPropertySet.h:119
bool isNull() const const
QVariant::Type type() const const
QString caption() const
Definition: KProperty.cpp:336
bool isEmpty() const const
KProperty()
Constructs a null property.
Definition: KProperty.cpp:307
KProperty & operator=(const QVariant &val)
Definition: KProperty.cpp:578
bool isModified() const
Return true if value of this property or value of any child property is modified.
Definition: KProperty.cpp:481
void setValueSyncPolicy(ValueSyncPolicy policy)
Sets synchronization policy for property values of this property See ValueSyncPolicy for details.
Definition: KProperty.cpp:537
void setVisible(bool visible)
Definition: KProperty.cpp:526
bool isNull() const
Definition: KProperty.cpp:475
QVariant oldValue() const
Definition: KProperty.cpp:400
bool setValue(const QVariant &value, ValueOptions options=ValueOptions())
Sets value of the property.
Definition: KProperty.cpp:405
int type() const
Definition: KProperty.cpp:366
QList::const_iterator constEnd() const const
bool isVisible() const
Definition: KProperty.cpp:520
void setOption(const char *name, const QVariant &val)
Definition: KProperty.cpp:555
const char * constData() const const
QString description() const
Definition: KProperty.cpp:354
bool valueEqualsTo(const QVariant &value, ValueOptions valueOptions=ValueOptions()) const
Definition: KProperty.cpp:417
QString name(StandardShortcut id)
bool hasOptions() const
Returns true if at least one option is specified for this property If there are no options defined tr...
Definition: KProperty.cpp:570
QString captionForDisplaying() const
Definition: KProperty.cpp:342
ValueSyncPolicy valueSyncPolicy() const
Definition: KProperty.cpp:531
void setCaption(const QString &caption)
Definition: KProperty.cpp:348
QVariant value() const
Definition: KProperty.cpp:394
QList::iterator begin()
void setReadOnly(bool readOnly)
Definition: KProperty.cpp:514
QString iconName() const
Definition: KProperty.cpp:382
void setStorable(bool storable)
Definition: KProperty.cpp:549
void resetValue()
Definition: KProperty.cpp:423
QList::iterator end()
A data container for properties of list type.
KProperty * child(const QByteArray &name)
Definition: KProperty.cpp:662
const QList< KProperty * > * children() const
Definition: KProperty.cpp:656
An interface for for composed property handlers.
void setName(const QByteArray &name)
Sets name of the property.
Definition: KProperty.cpp:330
QString toString() const const
void setComposedProperty(KComposedPropertyInterface *prop)
Definition: KProperty.cpp:684
ValueSyncPolicy
Synchronization policy for property values.
Definition: KProperty.h:355
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 03:48:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.