diff --git a/src/KProperty.cpp b/src/KProperty.cpp index 2b58daf..f63ae6d 100644 --- a/src/KProperty.cpp +++ b/src/KProperty.cpp @@ -1,798 +1,801 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2004-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KProperty.h" #include "KProperty_p.h" #include "KPropertySet_p.h" #include "KPropertyFactory.h" #include "kproperty_debug.h" +#include + //! @return true if @a currentValue and @a value are compatible static bool compatibleTypes(const QVariant& currentValue, const QVariant &value) { if (currentValue.isNull() || value.isNull()) return true; const QVariant::Type t = currentValue.type(); const QVariant::Type newt = value.type(); if (t == newt) return true; if ( (t == QVariant::Int && newt == QVariant::UInt) || (t == QVariant::UInt && newt == QVariant::Int) || (t == QVariant::ByteArray && newt == QVariant::String) || (t == QVariant::String && newt == QVariant::ByteArray) || (t == QVariant::ULongLong && newt == QVariant::LongLong) || (t == QVariant::LongLong && newt == QVariant::ULongLong)) { return true; } return false; } // ---- KProperty::Private::Private(KProperty *prop) : q(prop), type(KProperty::Auto), listData(0), changed(false), storable(true), readOnly(false), visible(true), composed(0), useComposedProperty(true), sets(0), parent(0), children(0), relatedProperties(0) { } void KProperty::Private::setCaptionForDisplaying(const QString& captionForDisplaying) { caption = captionForDisplaying.simplified(); if (caption == captionForDisplaying) { caption.clear(); } this->captionForDisplaying = captionForDisplaying; } KProperty::Private::~Private() { delete listData; if (children) { qDeleteAll(*children); delete children; } delete relatedProperties; delete composed; delete sets; } bool KProperty::Private::valueDiffersInternal(const QVariant &otherValue, KProperty::ValueOptions options) { if (!compatibleTypes(value, otherValue)) { kprWarning() << "INCOMPATIBLE TYPES! old=" << value << "new=" << otherValue << "in property" << q->name(); } const QVariant::Type t = value.type(); const QVariant::Type newt = otherValue.type(); if ( t == QVariant::DateTime || t == QVariant::Time) { //for date and datetime types: compare with strings, because there //can be miliseconds difference return value.toString() != otherValue.toString(); } else if (t == QVariant::String || t == QVariant::ByteArray) { //property is changed for string type, //if one of value is empty and other isn't.. return (value.toString().isEmpty() != otherValue.toString().isEmpty()) //..or both are not empty and values differ || (!value.toString().isEmpty() && !otherValue.toString().isEmpty() && value != otherValue); } else if (t == QVariant::Double) { - const double factor = 1.0 / option("step", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP).toDouble(); + const double factor = pow(10.0, option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION).toDouble()); //kprDebug() + // << "factor:" << factor << "precision:" << option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP) // << "double compared:" << value.toDouble() << otherValue.toDouble() // << ":" << static_cast(value.toDouble() * factor) << static_cast(otherValue.toDouble() * factor); return static_cast(value.toDouble() * factor) != static_cast(otherValue.toDouble() * factor); } else if (t == QVariant::Invalid && newt == QVariant::Invalid) { return false; } else if (composed && (options & ValueOption::UseComposedProperty)) { return !composed->valuesEqual(value, otherValue); } else { return value != otherValue; } } bool KProperty::Private::setValueInternal(const QVariant &newValue, KProperty::ValueOptions valueOptions) { if (name.isEmpty()) { kprWarning() << "COULD NOT SET value to a null property"; return false; } //1. Check if the value should be changed if (!valueDiffersInternal(newValue, valueOptions)) { return false; } //2. Then change it, and store old value if necessary if (valueOptions & KProperty::ValueOption::RememberOld) { if (!changed) { oldValue = value; } changed = true; } else { oldValue = QVariant(); // clear old value changed = false; } if (parent) { parent->d->childValueChanged(q, newValue, valueOptions); } QVariant prevValue; if (composed && useComposedProperty) { prevValue = value; //??? composed->setChildValueChangedEnabled(false); composed->setValue(q, newValue, valueOptions & ~ValueOptions(ValueOption::UseComposedProperty) // avoid infinite recursion ); composed->setChildValueChangedEnabled(true); } else { prevValue = value; } value = newValue; if (!parent) { // emit only if parent has not done it emitPropertyChanged(); // called as last step in this method! } return true; } void KProperty::Private::addChild(KProperty *prop) { if (!prop) { return; } if (!children || qFind(children->begin(), children->end(), prop) == children->end()) { // not in our list if (!children) { children = new QList(); } children->append(prop); prop->d->parent = q; } else { kprWarning() << "property" << name << ": child property" << prop->name() << "already added"; return; } } void KProperty::Private::addSet(KPropertySet *newSet) { if (!newSet) { return; } if (!set) {//simple case set = newSet; return; } if (set == newSet || (sets && sets->contains(newSet))) { return; } if (!sets) { sets = new QList< QPointer >; } sets->append(QPointer(newSet)); } void KProperty::Private::addRelatedProperty(KProperty *property) { if (!relatedProperties) relatedProperties = new QList(); if (!relatedProperties->contains(property)) { relatedProperties->append(property); } } void KProperty::Private::emitPropertyChanged() { QList< QPointer > *realSets = nullptr; if (sets) { realSets = sets; } else if (parent) { realSets = parent->d->sets; } if (realSets) { foreach (QPointer s, *realSets) { if (!s.isNull()) { //may be destroyed in the meantime emit s->propertyChangedInternal(*s, *q); emit s->propertyChanged(*s, *q); } } } else { QPointer realSet; if (set) { realSet = set; } else if (parent) { realSet = parent->d->set; } if (!realSet.isNull()) { //if the slot connect with that signal may call set->clear() - that's //the case e.g. at kexi/plugins/{macros|scripting}/* - this KProperty //may got destroyed ( see KPropertySet::removeProperty(KProperty*) ) while we are //still on it. So, if we try to access ourself/this once the signal //got emitted we may end in a very hard to reproduce crash. So, the //emit should happen as last step in this method! emit realSet->propertyChangedInternal(*realSet, *q); emit realSet->propertyChanged(*realSet, *q); } } } void KProperty::Private::childValueChanged(KProperty *child, const QVariant &value, KProperty::ValueOptions valueOptions) { if (!composed) { return; } composed->childValueChangedInternal( child, value, valueOptions & ~KProperty::ValueOptions(KProperty::ValueOption::UseComposedProperty) // avoid infinite recursion ); } ///////////////////////////////////////////////////////////////// KPropertyListData::KPropertyListData(const QStringList& keys_, const QStringList& names_) : names(names_) { setKeysAsStringList(keys_); } KPropertyListData::KPropertyListData(const QList keys_, const QStringList& names_) : keys(keys_), names(names_) { } KPropertyListData::KPropertyListData() { } KPropertyListData::~KPropertyListData() { } void KPropertyListData::setKeysAsStringList(const QStringList& list) { keys.clear(); for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { keys.append(*it); } } QStringList KPropertyListData::keysAsStringList() const { QStringList result; for (QList::ConstIterator it = keys.constBegin(); it != keys.constEnd(); ++it) { result.append((*it).toString()); } return result; } ///////////////////////////////////////////////////////////////// //static const KProperty::ValueOptions KProperty::DefaultValueOptions = KProperty::ValueOptions(KProperty::ValueOption::RememberOld | KProperty::ValueOption::UseComposedProperty); KProperty::KProperty(const QByteArray &name, const QVariant &value, const QString &caption, const QString &description, int type, KProperty* parent) : d(new KProperty::Private(this)) { d->name = name; d->setCaptionForDisplaying(caption); d->description = description; if (type == int(Auto)) { type = value.type(); } setType(type); if (parent) parent->d->addChild(this); setValue(value, DefaultValueOptions & ~ValueOptions(ValueOption::RememberOld)); } KProperty::KProperty(const QByteArray &name, const QStringList &keys, const QStringList &strings, const QVariant &value, const QString &caption, const QString &description, int type, KProperty* parent) : d(new KProperty::Private(this)) { d->name = name; d->setCaptionForDisplaying(caption); d->description = description; setListData(keys, strings); if (type == int(Auto)) { type = value.type(); } setType(type); if (parent) parent->d->addChild(this); setValue(value, KProperty::DefaultValueOptions & ~ValueOptions(ValueOption::RememberOld)); } KProperty::KProperty(const QByteArray &name, KPropertyListData* listData, const QVariant &value, const QString &caption, const QString &description, int type, KProperty* parent) : d(new KProperty::Private(this)) { d->name = name; d->setCaptionForDisplaying(caption); d->description = description; d->listData = listData; if (type == int(Auto)) { type = value.type(); } setType(type); if (parent) parent->d->addChild(this); setValue(value, KProperty::DefaultValueOptions & ~ValueOptions(ValueOption::RememberOld)); } KProperty::KProperty() : d(new KProperty::Private(this)) { } KProperty::KProperty(const KProperty &prop) : d(new KProperty::Private(this)) { *this = prop; } KProperty::~KProperty() { delete d; } QByteArray KProperty::name() const { return d->name; } void KProperty::setName(const QByteArray &name) { d->name = name; } QString KProperty::caption() const { return d->caption.isEmpty() ? d->captionForDisplaying : d->caption; } QString KProperty::captionForDisplaying() const { return d->captionForDisplaying; } void KProperty::setCaption(const QString &caption) { d->setCaptionForDisplaying(caption); } QString KProperty::description() const { return d->description; } void KProperty::setDescription(const QString &desc) { d->description = desc; } int KProperty::type() const { return d->type; } void KProperty::setType(int type) { if (d->type != type) { d->type = type; delete d->composed; d->composed = KPropertyFactoryManager::self()->createComposedProperty(this); } } QString KProperty::iconName() const { return d->iconName; } void KProperty::setIconName(const QString &name) { d->iconName = name; } QVariant KProperty::value() const { return d->value; } QVariant KProperty::oldValue() const { return d->oldValue; } void KProperty::setValue(const QVariant &value, ValueOptions options) { (void)d->setValueInternal(value, options); } void KProperty::setValue(const QVariant &value, bool *changed, ValueOptions options) { const bool ch = d->setValueInternal(value, options); if (changed) { *changed = ch; } } void KProperty::setValue(const QVariant &value, bool doNotUseThisOverload, bool doNotUseThisOverload2) { Q_UNUSED(value); Q_UNUSED(doNotUseThisOverload); Q_UNUSED(doNotUseThisOverload2); } bool KProperty::valueEqualsTo(const QVariant &value, ValueOptions valueOptions) const { return !d->valueDiffersInternal(value, valueOptions); } void KProperty::resetValue() { d->changed = false; bool cleared = false; if (d->set) { KPropertySetPrivate::d(d->set)->informAboutClearing(&cleared); //inform me about possibly clearing the property sets } setValue(oldValue(), ValueOption::UseComposedProperty); if (cleared) return; //property set has been cleared: no further actions make sense as 'this' is dead // maybe parent prop is also unchanged now if (d->parent && d->parent->value() == d->parent->oldValue()) d->parent->d->changed = false; if (d->sets) { foreach (QPointer set, *d->sets) { if (!set.isNull()) //may be destroyed in the meantime emit set->propertyReset(*set, *this); } } else if (d->set) { emit d->set->propertyReset(*d->set, *this); } } KPropertyListData* KProperty::listData() const { return d->listData; } void KProperty::setListData(KPropertyListData* list) { if (list == d->listData) return; delete d->listData; d->listData = list; } void KProperty::setListData(const QStringList &keys, const QStringList &names) { KPropertyListData* list = new KPropertyListData(keys, names); setListData(list); } //////////////////////////////////////////////////////////////// bool KProperty::isNull() const { return d->name.isEmpty(); } bool KProperty::isModified() const { return d->changed; } void KProperty::clearModifiedFlag() { d->changed = false; } bool KProperty::isReadOnly() const { return d->readOnly; } void KProperty::setReadOnly(bool readOnly) { d->readOnly = readOnly; } bool KProperty::isVisible() const { return d->visible; } void KProperty::setVisible(bool visible) { d->visible = visible; } KProperty::ValueSyncPolicy KProperty::valueSyncPolicy() const { return d->valueSyncPolicy; } void KProperty::setValueSyncPolicy(KProperty::ValueSyncPolicy policy) { d->valueSyncPolicy = policy; } bool KProperty::isStorable() const { return d->storable; } void KProperty::setStorable(bool storable) { d->storable = storable; } void KProperty::setOption(const char* name, const QVariant& val) { d->options[name] = val; } QVariant KProperty::option(const char* name, const QVariant& defaultValue) const { return d->option(name, defaultValue); } bool KProperty::hasOptions() const { return !d->options.isEmpty() || (d->parent && d->parent->hasOptions()); } ///////////////////////////////////////////////////////////////// KProperty& KProperty::operator= (const QVariant & val) { setValue(val); return *this; } KProperty& KProperty::operator= (const KProperty & property) { if (&property == this) return *this; delete d->listData; d->listData = 0; delete d->children; d->children = 0; delete d->relatedProperties; d->relatedProperties = 0; delete d->composed; d->composed = 0; d->name = property.d->name; d->setCaptionForDisplaying(property.captionForDisplaying()); d->description = property.d->description; d->type = property.d->type; d->iconName = property.d->iconName; d->valueSyncPolicy = property.d->valueSyncPolicy; d->visible = property.d->visible; d->storable = property.d->storable; d->readOnly = property.d->readOnly; d->options = property.d->options; if (property.d->listData) { d->listData = new KPropertyListData(*property.d->listData); } if (property.d->composed) { delete d->composed; d->composed = KPropertyFactoryManager::self()->createComposedProperty(this); // updates all children value, using KComposedPropertyInterface setValue(property.value()); } else { d->value = property.d->value; if (property.d->children) { // no KComposedPropertyInterface (should never happen), simply copy all children d->children = new QList(); QList::ConstIterator endIt = property.d->children->constEnd(); for (QList::ConstIterator it = property.d->children->constBegin(); it != endIt; ++it) { KProperty *child = new KProperty(*(*it)); d->addChild(child); } } } if (property.d->relatedProperties) { d->relatedProperties = new QList(*(property.d->relatedProperties)); } // update these later because they may have been changed when creating children d->oldValue = property.d->oldValue; d->changed = property.d->changed; return *this; } bool KProperty::operator ==(const KProperty &prop) const { return ((d->name == prop.d->name) && (value() == prop.value())); } bool KProperty::operator!=(const KProperty &prop) const { return !operator==(prop); } ///////////////////////////////////////////////////////////////// const QList* KProperty::children() const { return d->children; } KProperty* KProperty::child(const QByteArray &name) { QList::ConstIterator endIt = d->children->constEnd(); for (QList::ConstIterator it = d->children->constBegin(); it != endIt; ++it) { if ((*it)->name() == name) return *it; } return 0; } KProperty* KProperty::parent() const { return d->parent; } KComposedPropertyInterface* KProperty::composedProperty() const { return d->composed; } void KProperty::setComposedProperty(KComposedPropertyInterface *prop) { if (d->composed == prop) return; delete d->composed; d->composed = prop; } #if 0 int Property::sortingKey() const { return d->sortingKey; } void Property::setSortingKey(int key) { d->sortingKey = key; } #endif ///////////////////////////////////////////////////////////////// KPROPERTYCORE_EXPORT QDebug operator<<(QDebug dbg, const KProperty &p) { dbg.nospace() << "KProperty(" << "NAME=" << p.name(); if (!p.caption().isEmpty()) { dbg.nospace() << " CAPTION=" << p.caption(); } if (!p.description().isEmpty()) { dbg.nospace() << " DESC=" << p.description(); } dbg.nospace() << " TYPE=" << p.type(); if (p.value().isValid()) { dbg.nospace() << " VALUE=" << p.value(); } else { dbg.nospace() << " VALUE="; } if (p.oldValue().isValid()) { dbg.nospace() << " OLDVALUE=" << p.oldValue(); } if (p.isModified()) { dbg.nospace() << " MODIFIED"; } if (!p.isVisible()) { dbg.nospace() << " HIDDEN"; } //! @todo children... if (p.hasOptions()) { dbg.nospace() << " OPTIONS(" << p.d->options.count() << "): ["; QList optionKeys( p.d->options.keys() ); qSort(optionKeys); bool first = true; foreach (const QByteArray& key, optionKeys) { if (first) { first = false; } else { dbg.space() << ","; } dbg.nospace() << key << ":" << p.option(key.constData()); } dbg.nospace() << "]"; } dbg.nospace() << ")"; return dbg.space(); } diff --git a/src/KProperty.h b/src/KProperty.h index b05447c..dcc1274 100644 --- a/src/KProperty.h +++ b/src/KProperty.h @@ -1,522 +1,522 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2004-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPROPERTY_PROPERTY_H #define KPROPERTY_PROPERTY_H #include #include #include #include #include "kpropertycore_export.h" /*! \brief Namespace for a set of classes implementing generic properties framework. Main classes of this framework are: - KProperty, representing a single property with its own type and value - KPropertySet, a set of properties - KPropertyEditorView, a widget for displaying and editing properties provided by a KPropertySet object. Every property has its own row displayed within the editor view. The editor view enables editing of property values. The KProperty framework also supports adding composed and property types and custom property editor types. Take a look at the example application, available in the examples/ directory. @author Cedric Pasteur @author Alexander Dymo @author Jarosław Staniek */ class KComposedPropertyInterface; class KPropertySet; class KPropertySetPrivate; /*! Data container for properties of list type. */ class KPROPERTYCORE_EXPORT KPropertyListData { public: /*! Data container for list-value property. We will be able to choose an item from this list. */ KPropertyListData(const QStringList& keys_, const QStringList& names_); KPropertyListData(const QList keys_, const QStringList& names_); KPropertyListData(); ~KPropertyListData(); void setKeysAsStringList(const QStringList& list); QStringList keysAsStringList() const; /*! The string list containing all possible keys for this property or NULL if this is not a property of type 'list'. The values in this list are ordered, so the first key element is associated with first element from the 'names' list, and so on. */ QList keys; // QStringList keys; //! @todo what about using QValueList here too? /*! The list of translated names that will be visible on the screen. First value is referenced by first key, and so on. */ QStringList names; //unused for now /*! True (the default), if the list has fixed number of possible //unused for now items (keys). If this is false, user can add or enter own values. */ //unused for now bool fixed; }; /*! \brief The base class representing a single property KProperty object can hold a property of given type supported by QVariant. Properties of custom types - can be also created, see using KPropertyFactory. Composed or custome properties + can be also created, see using KPropertyFactory. Composed or custom properties are not created using subclassing of KProperty but using @ref KComposedPropertyInterface. Each property stores old value to allows undoing that reverts the value to the old one. Property has a non-empty name (a QByteArray), a caption that is user-visible translated string displayed in property editor. Description is a translatable string that can be specified too in order to further explain meaning of the property. Propery also supports setting arbitrary number of options using KProperty::setOption() that allow to customize look or behavior of the property in the editor. @code // Creating a simple property: KProperty *property = new KProperty(name, value, caption, description); // name is a QByteArray, value is whatever type QVariant supports // Creating a valueFromList property matching keys with names: QStringList keys, names; keys << "one" << "two" << "three"; // possible values of the property // Names (possibly translated) shown in the editor instead of the keys names << tr("One") << tr("Two") << tr("Three"); property = new KProperty(name, keys, names, "two", caption); // Creating a valueFromList property matching QVariant keys with names: QValueList variantKeys; variantKeys << 1 << 2 << 3; KPropertyListData *listData = new KPropertyListData(variantKeys, names); propertySet->addProperty(new KProperty("List", listData, "otheritem", "List")); @endcode @note Sometimes it makes sense to split property captions that have with more words to multiple lines using a newline character, e.g. "Allow Zero Size" to "Allow Zero\nSize". This is suitable especially for the needs of property editor which can offer only limited area. The text of property caption containing newline characters is available in its original form using KProperty::captionForDisplaying(). KProperty::caption() returns modified caption text in which the newline characters are substituted with spaces and any trailing and leading whitespace is removed. \author Cedric Pasteur \author Alexander Dymo \author Jarosław Staniek */ class KPROPERTYCORE_EXPORT KProperty { public: /*! Defines types of properties. Properties defined by plugins should have a type number >= UserDefined .*/ enum Type { //standard supported QVariant types Auto = 0x00ffffff, Invalid = QVariant::Invalid, BitArray = QVariant::BitArray, Bitmap = QVariant::Bitmap, Bool = QVariant::Bool, Brush = QVariant::Brush, ByteArray = QVariant::ByteArray, Char = QVariant::Char, Color = QVariant::Color, Cursor = QVariant::Cursor, Date = QVariant::Date, DateTime = QVariant::DateTime, Double = QVariant::Double, Font = QVariant::Font, Icon = QVariant::Icon, Image = QVariant::Image, Int = QVariant::Int, KeySequence = QVariant::KeySequence, Line = QVariant::Line, LineF = QVariant::LineF, List = QVariant::List, Locale = QVariant::Locale, LongLong = QVariant::LongLong, Map = QVariant::Map, Matrix = QVariant::Matrix, Transform = QVariant::Transform, Palette = QVariant::Palette, Pen = QVariant::Pen, Pixmap = QVariant::Pixmap, Point = QVariant::Point, PointF = QVariant::PointF, Polygon = QVariant::Polygon, Rect = QVariant::Rect, RectF = QVariant::RectF, RegExp = QVariant::RegExp, Region = QVariant::Region, Size = QVariant::Size, SizeF = QVariant::SizeF, SizePolicy = QVariant::SizePolicy, String = QVariant::String, StringList = QVariant::StringList, TextFormat = QVariant::TextFormat, TextLength = QVariant::TextLength, Time = QVariant::Time, UInt = QVariant::UInt, ULongLong = QVariant::ULongLong, Url = QVariant::Url, //predefined custom types ValueFromList = 1000 /*** children() const; /*! \return a child property for \a name, or NULL if there is no property with that name. */ KProperty* child(const QByteArray &name); /*! \return parent property for this property, or NULL if there is no parent property. */ KProperty* parent() const; /*! \return the composed property for this property, or NULL if there was no composed property defined. */ KComposedPropertyInterface* composedProperty() const; /*! Sets composed property \a prop for this property. */ void setComposedProperty(KComposedPropertyInterface *prop); /*! \return true if this property is null. Property is null if it has empty name. */ bool isNull() const; //! \return true if this property value is changed. bool isModified() const; //! Clears "modified" flag, so isModified() will return false. void clearModifiedFlag(); /*! \return true if the property is read-only when used in a property editor. @c false by default. The property can be read-write but still not editable for the user if the parent property set's read-only flag is set. @see KPropertySet::isReadOnly() */ bool isReadOnly() const; /*! Sets this property to be read-only. @see isReadOnly() */ void setReadOnly(bool readOnly); /*! \return true if the property is visible. Only visible properties are displayed by the property editor view. */ bool isVisible() const; /*! Sets the visibility flag.*/ void setVisible(bool visible); /*! \return true if the property can be saved to a stream, xml, etc. There is a possibility to use "GUI" properties that aren't stored but used only in a GUI.*/ bool isStorable() const; /*! Sets "storable" flag for this property. @see isStorable() */ void setStorable(bool storable); //! Synchronization policy for property values //! @since 3.1 enum class ValueSyncPolicy { Editor, //!< Allow to synchronize by the property editor using its valueSync setting (default) FocusOut, //!< Synchronize the value when focus is out of the editor widget for this property //!< or when the user presses the Enter key Auto //!< Synchronize automatically as soon as the editor widget for this property signals //! (using commitData) that the value has been changed, e.g. when the user types //! another letter in a text box }; //! @return synchronization policy for property values of this property //! @since 3.1 ValueSyncPolicy valueSyncPolicy() const; //! Sets synchronization policy for property values of this property //! See ValueSyncPolicy for details. //! @since 3.1 void setValueSyncPolicy(ValueSyncPolicy policy); /*! Sets value \a val for option \a name. Options are used to override default settings of individual properties. This is most visible in property editor widget. Currently supported options are:
  • min: integer value describing minimum value for properties of integer and double types. The default is 0.
  • minValueText: user-visible translated string to be displayed in editor for integer type when minimum is set for the property. @see QAbstractSpinBox::specialValueText
  • max: integer describing minimum value for properties of integer type. Default is 0xffff.
  • precision: integer value >= 0 describing the number of decimals after the decimal point for double type. Default value is 2. @see QDoubleSpinBox::decimals
  • step: double value > 0.0 describing the size of the step that is taken when the user hits the up or down button of editor for double type. Default value is 0.01. @see QDoubleSpinBox::singleStep
  • 3State: boolean value used for boolean type; if @c true, the editor becomes a combobox (instead of checkable button) and accepts the third "null" state.
  • yesName: user-visible translated string used for boolean type (both 2- and 3-state) to visually represent the "true" value. If not present, tr("Yes") is used.
  • noName: user-visible translated string used for boolean type (both 2- and 3-state) to visually represent the "false" value. If not present, tr("No") is used.
  • 3rdStateName: user-visible translated string used for boolean type (both 2- and 3-state) to visually represent the third "null" value. If not present, tr("None") is used.
  • nullName: user-visible translated string used for boolean type to display the "null" value, if and only if the property accepts two states (i.e. when "3State" option is @c false). If the "nullName" option is not set, null values are displayed as @c false.
  • extraValueAllowed: boolean value, if @c true the user is able to manually add extra values to a combobox.
  • fileMode: string value that describes what objects may select in the file dialog of the url editor:
    • "dirsOnly": only support and display directories; @see QFileDialog::ShowDirsOnly QFileDialog::Directory
    • "existingFile": only allow to select one existing file; @see QFileDialog::ExistingFile
    • Any other value: any file is supported, whether it exists or not @see QFileDialog::AnyFile
  • confirmOverwrites: boolean value, if @c true, user will be asked for confirmation of file overwriting in the url editor. @c false by default. @note The line edit does not validate the content.
  • multiLine: boolean value used for string type. If @c true, a multi-line QPlainTextEdit-based widget is used for editor; otherwise a single-line QLineEdit widget is used. @c false by default. Added in version 3.1.
*/ void setOption(const char* name, const QVariant& val); /*! @brief Returns value of given option * If the option @a name is missing and parent property is present (see parent()), * parent property is checked. If the parent property offers the option, the value * is returned. If it is not present there, @a defaultValue value is returned. * Looking at parent property is available since 3.1. * @note The lookup is performed recursively, first in parent, then grand parent, etc. * @see setOption */ QVariant option(const char* name, const QVariant& defaultValue = QVariant()) const; /*! @brief Returns @c true if at least one option is specified for this property * If there are no options defined @c true can be still returned if parent property * is present and it has at least one option specified. * Looking at parent property is available since 3.1. * @note The lookup is performed recursively, first in parent, then grand parent, etc. */ bool hasOptions() const; /*! Equivalent to setValue(const QVariant &) */ KProperty& operator= (const QVariant& val); /*! Assigns a deep copy of all attributes of \a property to this property. */ KProperty& operator= (const KProperty &property); /** * @return @c true if the property is equal to @a prop; otherwise returns @c false. * Two properties are equal if they have the same name and type. * @note All null properties are equal * @todo Compare properties deeper? */ bool operator==(const KProperty &prop) const; /** * @return @c true if the property is different from @a prop; otherwise returns @c false. * Two properties are different if they have different names or types. * @since 3.1 */ bool operator!=(const KProperty &prop) const; #if 0 /*! \return a key used for sorting. Usually its set by KPropertySet::addProperty() and KProperty::addChild() to a unique value, so that this property can be sorted in a property editor in original order. \see EditorItem::compare() */ int sortingKey() const; #endif private: //! Added only to help porting old code. Use public setValue() methods. void setValue(const QVariant &value, bool a1, bool a2 = true); class Private; Private * const d; friend class KPropertySet; friend class KPropertySetPrivate; friend class KPropertyBuffer; friend KPROPERTYCORE_EXPORT QDebug operator<<(QDebug dbg, const KProperty &p); }; //! qDebug() stream operator. Writes property @a p to the debug output in a nicely formatted way. KPROPERTYCORE_EXPORT QDebug operator<<(QDebug dbg, const KProperty &p); Q_DECLARE_OPERATORS_FOR_FLAGS(KProperty::ValueOptions) #endif diff --git a/src/KPropertyFactory.h b/src/KPropertyFactory.h index c15e781..0399cdd 100644 --- a/src/KPropertyFactory.h +++ b/src/KPropertyFactory.h @@ -1,181 +1,181 @@ /* This file is part of the KDE project Copyright (C) 2008-2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPROPERTY_FACTORY_H #define KPROPERTY_FACTORY_H #include "KProperty.h" #include #include #include //! An interface for for composed property handlers /*! KComposedPropertyInterface should be subclassed to override the behaviour of a property type. In the constructor child property objects should be created if there are any. Then functions handling values should be implemented. Example implementation of composed properties can be found in editors/ directory. */ class KPROPERTYCORE_EXPORT KComposedPropertyInterface { public: explicit KComposedPropertyInterface(KProperty *parent); virtual ~KComposedPropertyInterface(); /*! This function modifies the child properties for parent value @a value. It is called by @ref KProperty::setValue() when the property is composed. It is not necessary to modify the property value, it is done by KProperty. @note When this method is called valueOptions should not have the KProperty::ValueOption::UseComposedProperty flag set or else there will be infinite recursion. KProperty class makes sure this is the case but custom property developers should take care about this too. */ virtual void setValue(KProperty *property, const QVariant &value, KProperty::ValueOptions valueOptions) = 0; void childValueChangedInternal(KProperty *child, const QVariant &value, KProperty::ValueOptions valueOptions); void setChildValueChangedEnabled(bool set) { m_childValueChangedEnabled = set; } /*! @return true if values @a first and @a second are equal. Used in KProperty::setValue() to check if value has been changed before setting value. Default implementation uses operator==. */ inline virtual bool valuesEqual(const QVariant &first, const QVariant &second) { return first == second; } protected: virtual void childValueChanged(KProperty *child, const QVariant &value, KProperty::ValueOptions valueOptions) = 0; /*! This method emits the \a KPropertySet::propertyChanged() signal for all sets our parent-property is registered in. */ void emitPropertyChanged(); bool m_childValueChangedEnabled : 1; }; class KPROPERTYCORE_EXPORT KComposedPropertyCreatorInterface { public: KComposedPropertyCreatorInterface(); virtual ~KComposedPropertyCreatorInterface(); virtual KComposedPropertyInterface* createComposedProperty(KProperty *parent) const = 0; }; //! Creator returning composed property object template class KComposedPropertyCreator : public KComposedPropertyCreatorInterface { public: KComposedPropertyCreator() : KComposedPropertyCreatorInterface() {} virtual ~KComposedPropertyCreator() {} virtual ComposedProperty* createComposedProperty(KProperty *parent) const { return new ComposedProperty(parent); } }; //! Provides a specialized conversion of value to string depending on type class KPROPERTYCORE_EXPORT KPropertyValueDisplayInterface { public: KPropertyValueDisplayInterface(); virtual ~KPropertyValueDisplayInterface(); virtual QString propertyValueToString(const KProperty* property, const QLocale &locale) const { return valueToString(property->value(), locale); } virtual QString valueToString(const QVariant& value, const QLocale &locale) const = 0; //! Maximum length of strings to display in valueToString(), propertyValueToString() //! and KPropertyValuePainterInterface::paint(). //! Used to avoid inefficiences. Equal to 250. //! @todo Make configurable? static int maxStringValueLength(); - //! @return @a value converted to string usign QVariant::toString(), truncated if it's longer than @ref maxStringValueLength() + //! @return @a value converted to string using QVariant::toString(), truncated if it's longer than @ref maxStringValueLength() //! @see maxStringValueLength(); static QString valueToLocalizedString(const QVariant& value); }; class KPROPERTYCORE_EXPORT KPropertyFactory { public: KPropertyFactory(); virtual ~KPropertyFactory(); QHash composedPropertyCreators() const; QHash valueDisplays() const; void addComposedPropertyCreator(int type, KComposedPropertyCreatorInterface* creator); void addDisplay(int type, KPropertyValueDisplayInterface *display); protected: void addComposedPropertyCreatorInternal(int type, KComposedPropertyCreatorInterface* creator, bool own = true); //! Adds value-to-text converted @a painter for type @a type. //! The converter becomes owned by the factory. void addDisplayInternal(int type, KPropertyValueDisplayInterface *display, bool own = true); class Private; Private * const d; }; class KPROPERTYCORE_EXPORT KPropertyFactoryManager : public QObject { Q_OBJECT public: KComposedPropertyInterface* createComposedProperty(KProperty *parent); //! Registers factory @a factory. It becomes owned by the manager. void registerFactory(KPropertyFactory *factory); /*! \return a pointer to a factory manager instance.*/ static KPropertyFactoryManager* self(); bool canConvertValueToText(int type) const; bool canConvertValueToText(const KProperty* property) const; QString propertyValueToString(const KProperty* property) const; QString valueToString(int type, const QVariant &value) const; QString propertyValueToLocalizedString(const KProperty* property) const; QString valueToLocalizedString(int type, const QVariant &value) const; KPropertyFactoryManager(); ~KPropertyFactoryManager(); //! Adds function @a initFunction that will be called after the manager is created. //! Useful for creation custom factories. static void addInitFunction(void (*initFunction)()); private: class Private; Private * const d; }; #endif diff --git a/src/KProperty_p.h b/src/KProperty_p.h index fb848ad..5314adb 100644 --- a/src/KProperty_p.h +++ b/src/KProperty_p.h @@ -1,103 +1,106 @@ /* This file is part of the KDE project Copyright (C) 2009-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPROPERTY_PROPERTY_P_H #define KPROPERTY_PROPERTY_P_H #include "KPropertySet.h" #include //! Default value for "step" option. Used for spin boxes, etc. #define KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP 0.01 +//! Default value for "precision" option. Used for spin boxes, etc. +#define KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION 2 + //! @internal class Q_DECL_HIDDEN KProperty::Private { public: explicit Private(KProperty *prop); void setCaptionForDisplaying(const QString& captionForDisplaying); ~Private(); //! @return a value for option @a name or null value if there is no such option set. inline QVariant option(const char* name, const QVariant& defaultValue) const { if (options.contains(name)) return options[name]; return parent ? parent->option(name, defaultValue) : defaultValue; } //! @return true if value of this property differs from @a otherValue bool valueDiffersInternal(const QVariant &otherValue, KProperty::ValueOptions options); //! Sets value of the property to @a newValue bool setValueInternal(const QVariant &newValue, KProperty::ValueOptions valueOptions); /*! Adds @a prop as a child of this property. The children will be owned by this property. */ void addChild(KProperty *prop); /*! Adds @a set to this property. */ void addSet(KPropertySet *newSet); /*! Adds related property for this property. */ void addRelatedProperty(KProperty *property); /*! This method emits the @a KPropertySet::propertyChanged() signal. KProperty::setValue() calls this method if the value has been changed. */ void emitPropertyChanged(); void childValueChanged(KProperty *child, const QVariant &value, KProperty::ValueOptions valueOptions); KProperty * const q; int type; QByteArray name; QString captionForDisplaying; QString caption; QString description; QVariant value; QVariant oldValue; /*! The string-to-value correspondence list of the property.*/ KPropertyListData* listData; QString iconName; bool changed; bool storable; bool readOnly; bool visible; KProperty::ValueSyncPolicy valueSyncPolicy = KProperty::ValueSyncPolicy::Editor; QMap options; KComposedPropertyInterface *composed; //! Flag used to allow composed property to use setValue() without causing recursion bool useComposedProperty; //! Used when a single set is assigned for the property QPointer set; //! Used when multiple sets are assigned for the property QList< QPointer > *sets; KProperty *parent; QList *children; //! List of properties with the same name (when intersecting buffers) QList *relatedProperties; }; #endif diff --git a/src/editors/dateedit.cpp b/src/editors/dateedit.cpp index e89165f..5c7c426 100644 --- a/src/editors/dateedit.cpp +++ b/src/editors/dateedit.cpp @@ -1,98 +1,100 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2012 Friedrich W. H. Kossebau Copyright (C) 2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "dateedit.h" #include "KPropertyUtils.h" #include KPropertyDateEditor::KPropertyDateEditor(const KProperty* prop, QWidget* parent) : QDateEdit(parent) { setFrame(false); setCalendarPopup(true); - const QDate minDate = prop->option("min").toDate(); - if (minDate.isValid()) { - setMinimumDate(minDate); + if (prop->hasOptions()) { + const QDate minDate = prop->option("min", minimumDate()).toDate(); + const QDate maxDate = prop->option("max", maximumDate()).toDate(); + if (minDate.isValid() && maxDate.isValid() && minDate <= maxDate) { + setDateRange(minDate, maxDate); + } + const QString minValueText(prop->option("minValueText").toString()); + if (!minValueText.isEmpty()) { + setSpecialValueText(minValueText); + } } - const QDate maxDate = prop->option("max").toDate(); - if (maxDate.isValid()) { - setMaximumDate(maxDate); - } - connect(this, SIGNAL(dateChanged(QDate)), this, SLOT(onDateChanged())); } KPropertyDateEditor::~KPropertyDateEditor() { } QVariant KPropertyDateEditor::value() const { return QVariant(date()); } void KPropertyDateEditor::setValue(const QVariant& value) { blockSignals(true); setDate(value.toDate()); blockSignals(false); } void KPropertyDateEditor::paintEvent(QPaintEvent* event) { QDateEdit::paintEvent(event); KPropertyWidgetsFactory::paintTopGridLine(this); } void KPropertyDateEditor::onDateChanged() { emit commitData(this); } //! @todo Port to KLocale, be inspired by KexiDateTableEdit (with Kexi*Formatter) KPropertyDateDelegate::KPropertyDateDelegate() { } QString KPropertyDateDelegate::valueToString(const QVariant& value, const QLocale &locale) const { const QString defaultDateFormat = locale.dateFormat(QLocale::ShortFormat); return value.toDate().toString(defaultDateFormat); } QWidget* KPropertyDateDelegate::createEditor(int type, QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(type); Q_UNUSED(option); KProperty *prop = KPropertyUtils::propertyForIndex(index); if (!prop) { return 0; } return new KPropertyDateEditor(prop, parent); } diff --git a/src/editors/datetimeedit.cpp b/src/editors/datetimeedit.cpp index c0fdb16..5567b38 100644 --- a/src/editors/datetimeedit.cpp +++ b/src/editors/datetimeedit.cpp @@ -1,98 +1,100 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2012 Friedrich W. H. Kossebau Copyright (C) 2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "datetimeedit.h" #include "KPropertyWidgetsFactory.h" #include "KPropertyUtils.h" #include KPropertyDateTimeEditor::KPropertyDateTimeEditor(const KProperty* prop, QWidget* parent) : QDateTimeEdit(parent) { setFrame(false); setCalendarPopup(true); - const QDateTime minDateTime = prop->option("min").toDateTime(); - if (minDateTime.isValid()) { - setMinimumDateTime(minDateTime); + if (prop->hasOptions()) { + const QDateTime minDateTime = prop->option("min", minimumDateTime()).toDateTime(); + const QDateTime maxDateTime = prop->option("max", maximumDateTime()).toDateTime(); + if (minDateTime.isValid() && maxDateTime.isValid() && minDateTime <= maxDateTime) { + setDateTimeRange(minDateTime, maxDateTime); + } + const QString minValueText(prop->option("minValueText").toString()); + if (!minValueText.isEmpty()) { + setSpecialValueText(minValueText); + } } - const QDateTime maxDateTime = prop->option("max").toDateTime(); - if (maxDateTime.isValid()) { - setMaximumDateTime(maxDateTime); - } - connect(this, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(onDateTimeChanged())); } KPropertyDateTimeEditor::~KPropertyDateTimeEditor() { } QVariant KPropertyDateTimeEditor::value() const { return QVariant(dateTime()); } void KPropertyDateTimeEditor::setValue(const QVariant& value) { blockSignals(true); setDateTime(value.toDateTime()); blockSignals(false); } void KPropertyDateTimeEditor::paintEvent(QPaintEvent* event) { QDateTimeEdit::paintEvent(event); KPropertyWidgetsFactory::paintTopGridLine(this); } void KPropertyDateTimeEditor::onDateTimeChanged() { emit commitData(this); } //! @todo Port to KLocale, be inspired by KexiDateTimeTableEdit (with Kexi*Formatter) KPropertyDateTimeDelegate::KPropertyDateTimeDelegate() { } QString KPropertyDateTimeDelegate::valueToString(const QVariant& value, const QLocale &locale) const { const QString defaultDateTimeFormat = locale.dateTimeFormat(QLocale::ShortFormat); return value.toDateTime().toString(defaultDateTimeFormat); } QWidget* KPropertyDateTimeDelegate::createEditor(int type, QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(type); Q_UNUSED(option); KProperty *prop = KPropertyUtils::propertyForIndex(index); if (!prop) { return 0; } return new KPropertyDateTimeEditor(prop, parent); } diff --git a/src/editors/spinbox.cpp b/src/editors/spinbox.cpp index dc44b98..2f2ddac 100644 --- a/src/editors/spinbox.cpp +++ b/src/editors/spinbox.cpp @@ -1,329 +1,349 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2008-2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "spinbox.h" #include "KProperty.h" #include "KProperty_p.h" #include "KPropertyEditorView.h" #include "KPropertyUnit_p.h" #include "KPropertyUtils.h" +#include "KPropertyUtils_p.h" #include "KPropertyWidgetsFactory.h" #include "kproperty_debug.h" #include #include #include #include #include //! @return font size expressed in points (pt) //! or if points are not available - in pixels (px) for @a font static QString fontSizeForCSS(const QFont& font) { return font.pointSize() > 0 ? QString::fromLatin1("%1pt").arg(font.pointSize()) : QString::fromLatin1("%1px").arg(font.pixelSize()); } static QString cssForSpinBox(const char *_class, const QFont& font, int itemHeight) { return QString::fromLatin1( "%5 { border-left: 0; border-right: 0; font-size: %3; } " "%5::down-button { height: %1px; %4 } " "%5::up-button { height: %2px; } " "QLineEdit { border-width:0px; } " ) .arg(itemHeight/2 - 1).arg(itemHeight - itemHeight/2 - 1) .arg(fontSizeForCSS(font)) .arg(QLatin1String((itemHeight/2 <= 9) ? "bottom: 2px;" : "bottom: 0px;")) .arg(QLatin1String(_class)); } KPropertyIntSpinBox::KPropertyIntSpinBox(const KProperty* prop, QWidget *parent, int itemHeight) : QSpinBox(parent) , m_unsigned(prop->type() == KProperty::UInt) { QLineEdit* le = findChild(); setContentsMargins(0,0,0,0); if (le) { le->setAlignment(Qt::AlignLeft); le->setContentsMargins(0,0,0,0); } setFrame(true); QString css = cssForSpinBox("QSpinBox", font(), itemHeight); KPropertyWidgetsFactory::setTopAndBottomBordersUsingStyleSheet(this, css); setStyleSheet(css); QVariant minVal(prop->option("min", m_unsigned ? 0 : -INT_MAX)); QVariant maxVal(prop->option("max", INT_MAX)); setMinimum(minVal.toInt()); setMaximum(maxVal.toInt()); QString minValueText(prop->option("minValueText").toString()); if (!minValueText.isEmpty()) setSpecialValueText(minValueText); connect(this, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int))); } KPropertyIntSpinBox::~KPropertyIntSpinBox() { } QVariant KPropertyIntSpinBox::value() const { if (m_unsigned) return uint(QSpinBox::value()); return QSpinBox::value(); } void KPropertyIntSpinBox::setValue(const QVariant& value) { int v( value.toInt() ); if (m_unsigned && v<0) { kprWarning() << "could not assign negative value" << v << "- assigning 0"; v = 0; } QSpinBox::setValue(v); } void KPropertyIntSpinBox::slotValueChanged(int value) { Q_UNUSED(value); emit commitData(this); } //----------------------- class Q_DECL_HIDDEN KPropertyDoubleSpinBox::Private { public: KPropertyUnit unit; bool hasUnit; }; -static void decodeUnit(const KProperty &property, KPropertyUnit *unit, bool *hasUnit) +namespace { + +void decodeUnit(const KProperty &property, KPropertyUnit *unit, bool *hasUnit) { const QString unitString = property.option("unit").toString(); if (unitString.isEmpty()) { *hasUnit = false; } else { *unit = KPropertyUnit::fromSymbol(unitString, hasUnit); } } +//! Helper for testing property options +struct DoubleValueOptions { + explicit DoubleValueOptions(const KProperty &property) + { + minVal = property.option("min", 0.0); + maxVal = property.option("max", DBL_MAX / 100.0); + if (!minVal.canConvert(QMetaType::Double) || !maxVal.canConvert(QMetaType::Double) + || minVal.toReal() > maxVal.toReal()) + { + minVal.clear(); + maxVal.clear(); + } + step = property.option("step", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP); + if (!step.canConvert(QMetaType::Double) || step.toDouble() <= 0.0) { + step.clear(); + } + precision = property.option("precision", KPROPERTY_DEFAULT_DOUBLE_VALUE_PRECISION); + if (!precision.canConvert(QMetaType::Int) || precision.toInt() < 0) { + precision.clear(); + } + minValueText = property.option("minValueText").toString(); + } + QVariant minVal; + QVariant maxVal; + QVariant step; + QVariant precision; + QString minValueText; +}; +} // namespace + KPropertyDoubleSpinBox::KPropertyDoubleSpinBox(const KProperty* prop, QWidget *parent, int itemHeight) : QDoubleSpinBox(parent) , d(new Private) { setFrame(false); QLineEdit* le = findChild(); if (le) { le->setAlignment(Qt::AlignLeft); le->setContentsMargins(0,0,0,0); le->setFrame(false); } /* KPropertyFactory::setTopAndBottomBordersUsingStyleSheet(sb, QString::fromLatin1( "QDoubleSpinBox { border-left: 0; border-right: 0; } " "QDoubleSpinBox::down-button { height: %1px; } " "QDoubleSpinBox::up-button { height: %2px; }" ).arg(itemHeight/2).arg(itemHeight - itemHeight/2) );*/ QString css = cssForSpinBox("QDoubleSpinBox", font(), itemHeight); KPropertyWidgetsFactory::setTopAndBottomBordersUsingStyleSheet(this, css); setStyleSheet(css); - QVariant minVal(prop->option("min")); - if (minVal.canConvert(QMetaType::Double)) { - setMinimum(minVal.toDouble()); - } - QVariant maxVal(prop->option("max", DBL_MAX / 100.0)); - if (maxVal.canConvert(QMetaType::Double)) { - setMaximum(maxVal.toDouble()); + const DoubleValueOptions options(*prop); + if (options.minVal.isValid() && options.maxVal.isValid()) { + setRange(options.minVal.toDouble(), options.maxVal.toDouble()); } - QVariant step(prop->option("step", KPROPERTY_DEFAULT_DOUBLE_VALUE_STEP)); - if (step.canConvert(QMetaType::Double)) { - setSingleStep(step.toDouble()); + if (options.step.isValid()) { + setSingleStep(options.step.toDouble()); } //! @todo implement slider // bool slider = prop->option("slider", false).toBool(); - QVariant precision(prop->option("precision")); - if (precision.canConvert(QMetaType::Int)) { - setDecimals(precision.toInt()); + if (options.precision.isValid()) { + setDecimals(options.precision.toInt()); + } + if (!options.minValueText.isEmpty()) { + setSpecialValueText(options.minValueText); } - QString minValueText(prop->option("minValueText").toString()); - if (!minValueText.isEmpty()) - setSpecialValueText(minValueText); decodeUnit(*prop, &d->unit, &d->hasUnit); if (d->hasUnit) { setSuffix( QObject::tr("%1 %2", " ") // this adds necessary space .arg(QString()).arg(d->unit.toString())); } connect(this, SIGNAL(valueChanged(double)), this, SLOT(slotValueChanged(double))); } KPropertyDoubleSpinBox::~KPropertyDoubleSpinBox() { delete d; } void KPropertyDoubleSpinBox::resizeEvent( QResizeEvent * event ) { setFixedHeight(height() + 1); QDoubleSpinBox::resizeEvent(event); } void KPropertyDoubleSpinBox::setValue(double v) { if (d->hasUnit) { QDoubleSpinBox::setValue(d->unit.toUserValue(v)); return; } QDoubleSpinBox::setValue(v); } double KPropertyDoubleSpinBox::value() const { if (d->hasUnit) { return d->unit.fromUserValue(QDoubleSpinBox::value()); } return QDoubleSpinBox::value(); } void KPropertyDoubleSpinBox::slotValueChanged(double value) { Q_UNUSED(value); emit commitData(this); } //----------------------- KPropertyIntSpinBoxDelegate::KPropertyIntSpinBoxDelegate() { } QString KPropertyIntSpinBoxDelegate::propertyValueToString(const KProperty* prop, const QLocale &locale) const { KPropertyUnit unit; bool hasUnit; decodeUnit(*prop, &unit, &hasUnit); if (prop->hasOptions()) { //replace min value with minValueText if defined QVariant minValue(prop->option("min")); QString minValueText(prop->option("minValueText").toString()); if (!minValue.isNull() && !minValueText.isEmpty() && minValue.toInt() == prop->value().toInt()) { return minValueText; } } if (!hasUnit) { return valueToString(prop->value(), locale); } if (locale.language() == QLocale::C) { return QString::fromLatin1("%1 %2") .arg(valueToString(prop->value(), locale)) .arg(unit.toString()); } return QObject::tr("%1 %2", " ") .arg(valueToString(unit.toUserValue(prop->value().toDouble()), locale)) .arg(unit.toString()); } QString KPropertyIntSpinBoxDelegate::valueToString(const QVariant& value, const QLocale &locale) const { return locale.toString(value.toDouble(), 'f', 0); } QWidget* KPropertyIntSpinBoxDelegate::createEditor( int type, QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const { Q_UNUSED(type); KProperty *prop = KPropertyUtils::propertyForIndex(index); if (!prop) { return 0; } return new KPropertyIntSpinBox(prop, parent, option.rect.height() - 2); } //----------------------- KPropertyDoubleSpinBoxDelegate::KPropertyDoubleSpinBoxDelegate() { } QString KPropertyDoubleSpinBoxDelegate::propertyValueToString(const KProperty* prop, const QLocale &locale) const { + const DoubleValueOptions options(*prop); + //replace min value with minValueText if defined + if (options.minVal.isValid() && !options.minValueText.isEmpty() + && options.minVal.toDouble() == prop->value().toDouble()) + { + return options.minValueText; + } KPropertyUnit unit; bool hasUnit; decodeUnit(*prop, &unit, &hasUnit); - int precision = -1; - if (prop->hasOptions()) { - //replace min value with minValueText if defined - QVariant minValue(prop->option("min")); - QString minValueText(prop->option("minValueText").toString()); - if (!minValue.isNull() && !minValueText.isEmpty() - && minValue.toDouble() == prop->value().toDouble()) - { - return minValueText; - } - if (prop->option("precision").canConvert(QMetaType::Int)) { - precision = prop->option("precision").toInt(); - } - } const qreal realValue = hasUnit ? unit.toUserValue(prop->value().toReal()) : prop->value().toReal(); QString valueString; - if (precision >= 0) { - valueString = locale.toString(realValue, 'f', precision); + if (options.precision.isValid()) { + valueString = locale.toString(realValue, 'f', options.precision.toInt()); } else { valueString = valueToString(realValue, locale); } if (!hasUnit) { return valueString; } if (locale.language() == QLocale::C) { return QString::fromLatin1("%1 %2").arg(valueString).arg(unit.toString()); } return QObject::tr("%1 %2", " ").arg(valueString).arg(unit.toString()); } QString KPropertyDoubleSpinBoxDelegate::valueToString(const QVariant& value, const QLocale &locale) const { return locale.toString(value.toDouble()); } QWidget* KPropertyDoubleSpinBoxDelegate::createEditor( int type, QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const { Q_UNUSED(type); KProperty *prop = KPropertyUtils::propertyForIndex(index); if (!prop) { return 0; } return new KPropertyDoubleSpinBox(prop, parent, option.rect.height() - 2 - 1); } diff --git a/src/editors/timeedit.cpp b/src/editors/timeedit.cpp index 3a686cf..401bcd5 100644 --- a/src/editors/timeedit.cpp +++ b/src/editors/timeedit.cpp @@ -1,103 +1,106 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2012 Friedrich W. H. Kossebau Copyright (C) 2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "timeedit.h" #include "KPropertyUtils.h" #include KPropertyTimeEditor::KPropertyTimeEditor(const KProperty* prop, QWidget* parent) : QTimeEdit(parent) { setFrame(false); setContentsMargins(0,1,0,0); - const QTime minTime = prop->option("min").toTime(); - if (minTime.isValid()) { - setMinimumTime(minTime); - } - const QTime maxTime = prop->option("max").toTime(); - if (maxTime.isValid()) { - setMaximumTime(maxTime); + if (prop->hasOptions()) { + const QTime minTime = prop->option("min", minimumTime()).toTime(); + const QTime maxTime = prop->option("max", maximumTime()).toTime(); + if (minTime.isValid() && maxTime.isValid() && minTime <= maxTime) { + setTimeRange(minTime, maxTime); + } + const QString minValueText(prop->option("minValueText").toString()); + if (!minValueText.isEmpty()) { + setSpecialValueText(minValueText); + } } connect(this, SIGNAL(timeChanged(QTime)), this, SLOT(onTimeChanged())); } KPropertyTimeEditor::~KPropertyTimeEditor() { } QVariant KPropertyTimeEditor::value() const { return QVariant(time()); } void KPropertyTimeEditor::setValue(const QVariant& value) { blockSignals(true); setTime(value.toTime()); blockSignals(false); } void KPropertyTimeEditor::paintEvent(QPaintEvent* event) { QTimeEdit::paintEvent(event); KPropertyWidgetsFactory::paintTopGridLine(this); } void KPropertyTimeEditor::onTimeChanged() { emit commitData(this); } //! @todo Port to KLocale, be inspired by KexiTimeTableEdit (with Kexi*Formatter) KPropertyTimeDelegate::KPropertyTimeDelegate() { } QString KPropertyTimeDelegate::valueToString(const QVariant& value, const QLocale &locale) const { if (locale.language() == QLocale::C) { if (value.isNull()) { return QString(); } return value.toTime().toString(Qt::ISODate); } const QString defaultTimeFormat = locale.timeFormat(QLocale::ShortFormat); return value.toTime().toString(defaultTimeFormat); } QWidget* KPropertyTimeDelegate::createEditor(int type, QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(type); Q_UNUSED(option); KProperty* prop = KPropertyUtils::propertyForIndex(index); if (!prop) { return 0; } return new KPropertyTimeEditor(prop, parent); }