/* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Alexander Dymo Copyright (C) 2004-2006 Jaroslaw 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 "property.h" #include "customproperty.h" #include "set.h" #include "factory.h" #include #include #include #include #include namespace KoProperty { QT_STATIC_CONST_IMPL Property Property::null; //! @internal class PropertyPrivate { public: PropertyPrivate() : caption(0), listData(0), changed(false), storable(true), readOnly(false), visible(true), autosync(-1), custom(0), useCustomProperty(true), sets(0), tqparent(0), tqchildren(0), relatedProperties(0), sortingKey(0) { } inline void setCaptionForDisplaying(const TQString& captionForDisplaying) { delete caption; if (captionForDisplaying.simplifyWhiteSpace()!=captionForDisplaying) caption = new TQString(captionForDisplaying.simplifyWhiteSpace()); else caption = 0; this->captionForDisplaying = captionForDisplaying; } ~PropertyPrivate() { delete caption; caption = 0; delete listData; delete tqchildren; delete relatedProperties; delete custom; delete sets; } int type; TQCString name; TQString captionForDisplaying; TQString* caption; TQString description; TQVariant value; TQVariant oldValue; /*! The string-to-value correspondence list of the property.*/ Property::ListData* listData; // TQMap *valueList; TQString icon; bool changed : 1; bool storable : 1; bool readOnly : 1; bool visible : 1; int autosync; TQMap options; CustomProperty *custom; //! Flag used to allow CustomProperty to use setValue() bool useCustomProperty; //! Used when a single set is assigned for the property TQGuardedPtr set; //! Used when multiple sets are assigned for the property TQPtrDict< TQGuardedPtr > *sets; // TQValueList sets; Property *tqparent; TQValueList *tqchildren; //! list of properties with the same name (when intersecting buffers) TQValueList *relatedProperties; int sortingKey; }; } using namespace KoProperty; ///////////////////////////////////////////////////////////////// Property::ListData::ListData(const TQStringList& keys_, const TQStringList& names_) : names(names_) // , fixed(true) { setKeysAsStringList(keys_); } Property::ListData::ListData(const TQValueList keys_, const TQStringList& names_) : keys(keys_), names(names_) // , fixed(true) { } Property::ListData::ListData() // : fixed(true) { } Property::ListData::~ListData() { } void Property::ListData::setKeysAsStringList(const TQStringList& list) { keys.clear(); for (TQStringList::ConstIterator it = list.constBegin(); it!=list.constEnd(); ++it) { keys.append(*it); } } TQStringList Property::ListData::keysAsStringList() const { TQStringList result; for (TQValueList::ConstIterator it = keys.constBegin(); it!=keys.constEnd(); ++it) { result.append((*it).toString()); } return result; } ///////////////////////////////////////////////////////////////// /* KOPROPERTY_EXPORT TQMap KoProperty::createValueListFromStringLists(const TQStringList &keys, const TQStringList &values) { TQMap map; if(keys.count() != values.count()) return map; TQStringList::ConstIterator valueIt = values.begin(); TQStringList::ConstIterator endIt = keys.constEnd(); for(TQStringList::ConstIterator it = keys.begin(); it != endIt; ++it, ++valueIt) map.insert( *it, *valueIt); return map; } */ Property::Property(const TQCString &name, const TQVariant &value, const TQString &caption, const TQString &description, int type, Property* tqparent) : d( new PropertyPrivate() ) { d->name = name; d->setCaptionForDisplaying(caption); d->description = description; if(type == Auto) d->type = value.type(); else d->type = type; d->custom = FactoryManager::self()->createCustomProperty(this); if (tqparent) tqparent->addChild(this); setValue(value, false); } Property::Property(const TQCString &name, const TQStringList &keys, const TQStringList &strings, const TQVariant &value, const TQString &caption, const TQString &description, int type, Property* tqparent) : d( new PropertyPrivate() ) { d->name = name; d->setCaptionForDisplaying(caption); d->description = description; d->type = type; setListData(keys, strings); d->custom = FactoryManager::self()->createCustomProperty(this); if (tqparent) tqparent->addChild(this); setValue(value, false); } Property::Property(const TQCString &name, ListData* listData, const TQVariant &value, const TQString &caption, const TQString &description, int type, Property* tqparent) : d( new PropertyPrivate() ) { d->name = name; d->setCaptionForDisplaying(caption); d->description = description; d->type = type; d->listData = listData; d->custom = FactoryManager::self()->createCustomProperty(this); if (tqparent) tqparent->addChild(this); setValue(value, false); } Property::Property() : d( new PropertyPrivate() ) { } Property::Property(const Property &prop) : d( new PropertyPrivate() ) { *this = prop; } Property::~Property() { delete d; d = 0; } TQCString Property::name() const { return d->name; } void Property::setName(const TQCString &name) { d->name = name; } TQString Property::caption() const { return d->caption ? *d->caption : d->captionForDisplaying; } TQString Property::captionForDisplaying() const { return d->captionForDisplaying; } void Property::setCaption(const TQString &caption) { d->setCaptionForDisplaying(caption); } TQString Property::description() const { return d->description; } void Property::setDescription(const TQString &desc) { d->description = desc; } int Property::type() const { return d->type; } void Property::setType(int type) { d->type = type; } TQString Property::icon() const { return d->icon; } void Property::setIcon(const TQString &icon) { d->icon = icon; } TQVariant Property::value() const { if(d->custom && d->custom->handleValue()) return d->custom->value(); return d->value; } TQVariant Property::oldValue() const { /* if(d->oldValue.isNull()) return value(); else*/ return d->oldValue; } void Property::setValue(const TQVariant &value, bool rememberOldValue, bool useCustomProperty) { if (d->name.isEmpty()) { kopropertywarn << "Property::setValue(): COULD NOT SET value to a null property" << endl; return; } TQVariant currentValue = this->value(); const TQVariant::Type t = currentValue.type(); const TQVariant::Type newt = value.type(); // kopropertydbg << d->name << " : setValue('" << value.toString() << "' type=" << type() << ")" << endl; if (t != newt && !currentValue.isNull() && !value.isNull() && !( (t==TQVariant::Int && newt==TQVariant::UInt) || (t==TQVariant::UInt && newt==TQVariant::Int) || (t==TQVariant::CString && newt==TQVariant::String) || (t==TQVariant::String && newt==TQVariant::CString) || (t==TQVariant::ULongLong && newt==TQVariant::LongLong) || (t==TQVariant::LongLong && newt==TQVariant::ULongLong) )) { kopropertywarn << "Property::setValue(): INCOMPATIBLE TYPES! old=" << currentValue << " new=" << value << endl; } //1. Check if the value should be changed bool ch; if (t == TQVariant::DateTime || t == TQVariant::Time) { //for date and datetime types: compare with strings, because there //can be miliseconds difference ch = (currentValue.toString() != value.toString()); } else if (t == TQVariant::String || t==TQVariant::CString) { //property is changed for string type, //if one of value is empty and other isn't.. ch = ( (currentValue.toString().isEmpty() != value.toString().isEmpty()) //..or both are not empty and values differ || (!currentValue.toString().isEmpty() && !value.toString().isEmpty() && currentValue != value) ); } else if (t == TQVariant::Invalid && newt == TQVariant::Invalid) ch = false; else ch = (currentValue != value); if (!ch) return; //2. Then change it, and store old value if necessary if(rememberOldValue) { if(!d->changed) d->oldValue = currentValue; d->changed = true; } else { d->oldValue = TQVariant(); // clear old value d->changed = false; } TQVariant prevValue; if(d->custom && useCustomProperty) { d->custom->setValue(value, rememberOldValue); prevValue = d->custom->value(); } else prevValue = currentValue; if (!d->custom || !useCustomProperty || !d->custom->handleValue()) d->value = value; emitPropertyChanged(); // called as last step in this method! } void Property::resetValue() { d->changed = false; bool cleared = false; if (d->set) d->set->informAboutClearing(cleared); //inform me about possibly clearing the property sets setValue(oldValue(), false); if (cleared) return; //property set has been cleared: no further actions make sense as 'this' is dead // maybe tqparent prop is also unchanged now if(d->tqparent && d->tqparent->value() == d->tqparent->oldValue()) d->tqparent->d->changed = false; if (d->sets) { for (TQPtrDictIterator< TQGuardedPtr > it(*d->sets); it.current(); ++it) { if (it.current()) //may be destroyed in the meantime emit (*it.current())->propertyReset(**it.current(), *this); } } else if (d->set) { emit d->set->propertyReset(*d->set, *this); } } //const TQMap* Property::ListData* Property::listData() const { return d->listData; } void Property::setListData(ListData* list) //const TQMap &list) { // if(!d->valueList) // d->valueList = new TQMap(); if (list == d->listData) return; delete d->listData; d->listData = list; } void Property::setListData(const TQStringList &keys, const TQStringList &names) { ListData* list = new ListData(keys, names); setListData(list); // if(!d->valueList) // d->valueList = new TQMap(); // *(d->valueList) = createValueListFromStringLists(keys, values); } //////////////////////////////////////////////////////////////// bool Property::isNull() const { return d->name.isEmpty(); } bool Property::isModified() const { return d->changed; } void Property::clearModifiedFlag() { d->changed = false; } bool Property::isReadOnly() const { return d->readOnly; } void Property::setReadOnly(bool readOnly) { d->readOnly = readOnly; } bool Property::isVisible() const { return d->visible; } void Property::setVisible(bool visible) { d->visible = visible; } int Property::autoSync() const { return d->autosync; } void Property::setAutoSync(int sync) { d->autosync = sync; } bool Property::isStorable() const { return d->storable; } void Property::setStorable(bool storable) { d->storable = storable; } void Property::setOption(const char* name, const TQVariant& val) { d->options[name] = val; } TQVariant Property::option(const char* name) const { if (d->options.tqcontains(name)) return d->options[name]; return TQVariant(); } bool Property::hasOptions() const { return !d->options.isEmpty(); } ///////////////////////////////////////////////////////////////// Property::operator bool () const { return !isNull(); } const Property& Property::operator= (const TQVariant& val) { setValue(val); return *this; } const Property& Property::operator= (const Property &property) { if(&property == this) return *this; if(d->listData) { delete d->listData; d->listData = 0; } if(d->tqchildren) { delete d->tqchildren; d->tqchildren = 0; } if(d->relatedProperties) { delete d->relatedProperties; d->relatedProperties = 0; } if(d->custom) { delete d->custom; d->custom = 0; } d->name = property.d->name; d->setCaptionForDisplaying(property.captionForDisplaying()); d->description = property.d->description; d->type = property.d->type; d->icon = property.d->icon; d->autosync = property.d->autosync; 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 ListData(*property.d->listData); //TQMap(*(property.d->valueList)); } if(property.d->custom) { d->custom = FactoryManager::self()->createCustomProperty(this); // updates all tqchildren value, using CustomProperty setValue(property.value()); } else { d->value = property.d->value; if(property.d->tqchildren) { // no CustomProperty (should never happen), simply copy all tqchildren d->tqchildren = new TQValueList(); TQValueList::ConstIterator endIt = property.d->tqchildren->constEnd(); for(TQValueList::ConstIterator it = property.d->tqchildren->constBegin(); it != endIt; ++it) { Property *child = new Property( *(*it) ); addChild(child); } } } if(property.d->relatedProperties) { d->relatedProperties = new TQValueList( *(property.d->relatedProperties)); } // update these later because they may have been changed when creating tqchildren d->oldValue = property.d->oldValue; d->changed = property.d->changed; d->sortingKey = property.d->sortingKey; return *this; } bool Property::operator ==(const Property &prop) const { return ((d->name == prop.d->name) && (value() == prop.value())); } ///////////////////////////////////////////////////////////////// const TQValueList* Property::tqchildren() const { return d->tqchildren; } Property* Property::child(const TQCString &name) { TQValueList::ConstIterator endIt = d->tqchildren->constEnd(); for(TQValueList::ConstIterator it = d->tqchildren->constBegin(); it != endIt; ++it) { if((*it)->name() == name) return *it; } return 0; } Property* Property::tqparent() const { return d->tqparent; } void Property::addChild(Property *prop) { if (!prop) return; if(!d->tqchildren || tqFind( d->tqchildren->begin(), d->tqchildren->end(), prop) == d->tqchildren->end()) { // not in our list if(!d->tqchildren) d->tqchildren = new TQValueList(); d->tqchildren->append(prop); prop->setSortingKey(d->tqchildren->count()); prop->d->tqparent = this; } else { kopropertywarn << "Property::addChild(): property \"" << name() << "\": child property \"" << prop->name() << "\" already added" << endl; return; } } void Property::addSet(Set *set) { if (!set) return; if (!d->set) {//simple case d->set = set; return; } if ((Set*)d->set==set) return; TQGuardedPtr *pset = d->sets ? d->sets->tqfind(set) : 0; if (pset && (Set*)*pset == set) return; if (!d->sets) { d->sets = new TQPtrDict< TQGuardedPtr >( 101 ); d->sets->setAutoDelete(true); } d->sets->tqreplace(set, new TQGuardedPtr( set )); // TQValueList::iterator it = tqFind( d->sets.begin(), d->sets.end(), set); // if(it == d->sets.end()) // not in our list // d->sets.append(set); } const TQValueList* Property::related() const { return d->relatedProperties; } void Property::addRelatedProperty(Property *property) { if(!d->relatedProperties) d->relatedProperties = new TQValueList(); TQValueList::iterator it = tqFind( d->relatedProperties->begin(), d->relatedProperties->end(), property); if(it == d->relatedProperties->end()) // not in our list d->relatedProperties->append(property); } CustomProperty* Property::customProperty() const { return d->custom; } void Property::setCustomProperty(CustomProperty *prop) { d->custom = prop; } int Property::sortingKey() const { return d->sortingKey; } void Property::setSortingKey(int key) { d->sortingKey = key; } void Property::emitPropertyChanged() { if (d->sets) { for (TQPtrDictIterator< TQGuardedPtr > it(*d->sets); it.current(); ++it) { if (it.current()) {//may be destroyed in the meantime emit (*it.current())->propertyChangedInternal(**it.current(), *this); emit (*it.current())->propertyChanged(**it.current(), *this); } } } else if (d->set) { //if the slot connect with that signal may call set->clear() - that's //the case e.g. at kexi/plugins/{macros|scripting}/* - this Property //may got destroyed ( see Set::removeProperty(Property*) ) 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 d->set->propertyChangedInternal(*d->set, *this); emit d->set->propertyChanged(*d->set, *this); } } ///////////////////////////////////////////////////////////////// void Property::debug() { TQString dbg = "Property( name='" + TQString(d->name) + "' desc='" + d->description + "' val=" + (value().isValid() ? value().toString() : ""); if (!d->oldValue.isValid()) dbg += (", oldVal='" + d->oldValue.toString() + '\''); dbg += (TQString(d->changed ? " " : " un") + "changed"); dbg += (d->visible ? " visible" : " hidden"); dbg+=" )"; kopropertydbg << dbg << endl; }