/*
- Copyright (C) 2010 Paul Davis
+ Copyright (C) 2010 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <sstream>
#include <list>
#include <set>
-#include <glib.h>
+#include <iostream>
+#include "pbd/libpbd_visibility.h"
#include "pbd/xml++.h"
+#include "pbd/property_basics.h"
+#include "pbd/property_list.h"
+#include "pbd/enumwriter.h"
+#include "pbd/stateful.h"
namespace PBD {
-typedef GQuark PropertyID;
-
-template<typename T>
-struct PropertyDescriptor {
- PropertyID id;
- typedef T value_type;
-};
-
-class PropertyChange : public std::set<PropertyID>
-{
- public:
- PropertyChange() { }
- template<typename T> PropertyChange(PropertyDescriptor<T> p) { insert (p.id); }
- PropertyChange(const PropertyChange& other) : std::set<PropertyID> (other) { }
-
- PropertyChange operator= (const PropertyChange& other) {
- clear ();
- insert (other.begin(), other.end());
- return *this;
- }
-
- template<typename T> PropertyChange operator= (PropertyDescriptor<T> p) {
- clear ();
- insert (p.id);
- return *this;
- }
-
- template<typename T> bool contains (PropertyDescriptor<T> p) const { return find (p.id) != end(); }
-
- bool contains (const PropertyChange& other) const {
- for (const_iterator x = other.begin(); x != other.end(); ++x) {
- if (find (*x) != end()) {
- return true;
- }
- }
- return false;
- }
-
- void add (PropertyID id) { (void) insert (id); }
- void add (const PropertyChange& other) { (void) insert (other.begin(), other.end()); }
- template<typename T> void add (PropertyDescriptor<T> p) { (void) insert (p.id); }
-};
-
-/** Base (non template) part of Property */
-class PropertyBase
+/** Parent class for classes which represent a single scalar property in a Stateful object */
+template<class T>
+class /*LIBPBD_API*/ PropertyTemplate : public PropertyBase
{
public:
- PropertyBase (PropertyID pid)
- : _have_old (false)
- , _property_id (pid)
- {
+ PropertyTemplate (PropertyDescriptor<T> p, T const& v)
+ : PropertyBase (p.property_id)
+ , _have_old (false)
+ , _current (v)
+ {}
- }
+ PropertyTemplate (PropertyDescriptor<T> p, T const& o, T const& c)
+ : PropertyBase (p.property_id)
+ , _have_old (true)
+ , _current (c)
+ , _old (o)
+ {}
- /** Forget about any old value for this state */
- void clear_history () {
- _have_old = false;
- }
+ PropertyTemplate (PropertyDescriptor<T> p, PropertyTemplate<T> const & s)
+ : PropertyBase (p.property_id)
+ , _have_old (false)
+ , _current (s._current)
+ {}
- virtual void diff (XMLNode *, XMLNode *) const = 0;
- virtual void diff (PropertyChange&) const = 0;
- virtual bool set_state (XMLNode const &) = 0;
- virtual void add_state (XMLNode &) const = 0;
- const gchar* property_name() const { return g_quark_to_string (_property_id); }
- PropertyID id() const { return _property_id; }
+ /* OPERATORS / ACCESSORS */
- bool operator== (PropertyID pid) const {
- return _property_id == pid;
+ T & operator=(T const& v) {
+ set (v);
+ return _current;
}
-protected:
- bool _have_old;
- PropertyID _property_id;
-};
-
-/** Parent class for classes which represent a single property in a Stateful object */
-template <class T>
-class PropertyTemplate : public PropertyBase
-{
-public:
- PropertyTemplate (PropertyDescriptor<T> p, T const & v)
- : PropertyBase (p.id)
- , _current (v)
- {
-
- }
- PropertyTemplate<T> & operator= (PropertyTemplate<T> const & s) {
- /* XXX: isn't there a nicer place to do this? */
- _have_old = s._have_old;
- _property_id = s._property_id;
-
- _current = s._current;
- _old = s._old;
+ /* This will mean that, if fred and jim are both PropertyTemplates,
+ * fred = jim will result in fred taking on jim's current value,
+ * but NOT jim's property ID.
+ */
+ PropertyTemplate<T> & operator= (PropertyTemplate<T> const & p) {
+ set (p._current);
return *this;
}
- T & operator= (T const & v) {
- set (v);
- return _current;
- }
-
- T & operator+= (T const & v) {
+ T & operator+=(T const& v) {
set (_current + v);
return _current;
}
-
- bool operator== (const T& other) const {
+
+ bool operator==(const T& other) const {
return _current == other;
}
- bool operator!= (const T& other) const {
+ bool operator!=(const T& other) const {
return _current != other;
}
- operator T const & () const {
+ operator T const &() const {
return _current;
}
- T const & val () const {
+ T const& val () const {
return _current;
}
- void diff (XMLNode* old, XMLNode* current) const {
- if (_have_old) {
- old->add_property (property_name(), to_string (_old));
- current->add_property (property_name(), to_string (_current));
- }
- }
-
- void diff (PropertyChange& c) const {
- if (_have_old) {
- c.add (_property_id);
- }
- }
- /** Try to set state from the property of an XML node.
- * @param node XML node.
- * @return true if the value of the property is changed
- */
- bool set_state (XMLNode const & node) {
- XMLProperty const * p = node.property (property_name());
+ /* MANAGEMENT OF Stateful State */
+
+ bool set_value (XMLNode const & node) {
+
+ XMLProperty const* p = node.property (property_name());
if (p) {
T const v = from_string (p->value ());
set (v);
return true;
}
- }
+ }
return false;
}
- void add_state (XMLNode & node) const {
- node.add_property (property_name(), to_string (_current));
+ void get_value (XMLNode & node) const {
+ node.add_property (property_name(), to_string (_current));
+ }
+
+
+ /* MANAGEMENT OF HISTORY */
+
+ void clear_changes () {
+ _have_old = false;
+ }
+
+ bool changed () const { return _have_old; }
+
+ void invert () {
+ T const tmp = _current;
+ _current = _old;
+ _old = tmp;
+ }
+
+
+ /* TRANSFERRING HISTORY TO / FROM A StatefulDiffCommand */
+
+ void get_changes_as_xml (XMLNode* history_node) const {
+ XMLNode* node = history_node->add_child (property_name());
+ node->add_property ("from", to_string (_old));
+ node->add_property ("to", to_string (_current));
+ }
+
+ void get_changes_as_properties (PropertyList& changes, Command *) const {
+ if (this->_have_old) {
+ changes.add (clone ());
+ }
+ }
+
+
+ /* VARIOUS */
+
+ void apply_changes (PropertyBase const * p) {
+ T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val ();
+ if (v != _current) {
+ set (v);
+ }
}
protected:
- void set (T const & v) {
- _old = _current;
- _have_old = true;
- _current = v;
+
+ void set (T const& v) {
+ if (v != _current) {
+ if (!_have_old) {
+ _old = _current;
+ _have_old = true;
+ } else {
+ if (v == _old) {
+ /* value has been reset to the value
+ at the start of a history transaction,
+ before clear_changes() is called.
+ thus there is effectively no apparent
+ history for this property.
+ */
+ _have_old = false;
+ }
+ }
+
+ _current = v;
+ }
}
- virtual std::string to_string (T const & v) const = 0;
- virtual T from_string (std::string const & s) const = 0;
-
+ virtual std::string to_string (T const& v) const = 0;
+ virtual T from_string (std::string const& s) const = 0;
+
+ bool _have_old;
T _current;
T _old;
+
+private:
+ /* disallow copy-construction; it's not obvious whether it should mean
+ a copy of just the value, or the value and property ID.
+ */
+ PropertyTemplate (PropertyTemplate<T> const &);
};
-template<class T>
-std::ostream& operator<< (std::ostream& os, PropertyTemplate<T> const & s)
+template<class T> /*LIBPBD_API*/
+std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
{
- return os << s.val();
+ return os << s.val ();
}
-/** Representation of a single piece of state in a Stateful; for use
+/** Representation of a single piece of scalar state in a Stateful; for use
* with types that can be written to / read from stringstreams.
*/
-template <class T>
-class Property : public PropertyTemplate<T>
+template<class T>
+class /*LIBPBD_API*/ Property : public PropertyTemplate<T>
{
public:
- Property (PropertyDescriptor<T> q, T const & v)
+ Property (PropertyDescriptor<T> q, T const& v)
: PropertyTemplate<T> (q, v)
- {
+ {}
+
+ Property (PropertyDescriptor<T> q, T const& o, T const& c)
+ : PropertyTemplate<T> (q, o, c)
+ {}
+
+ Property (PropertyDescriptor<T> q, Property<T> const& v)
+ : PropertyTemplate<T> (q, v)
+ {}
+ Property<T>* clone () const {
+ return new Property<T> (this->property_id(), this->_old, this->_current);
}
-
- T & operator= (T const & v) {
+
+ Property<T>* clone_from_xml (const XMLNode& node) const {
+ XMLNodeList const & children = node.children ();
+ XMLNodeList::const_iterator i = children.begin();
+ while (i != children.end() && (*i)->name() != this->property_name()) {
+ ++i;
+ }
+
+ if (i == children.end()) {
+ return 0;
+ }
+ XMLProperty* from = (*i)->property ("from");
+ XMLProperty* to = (*i)->property ("to");
+
+ if (!from || !to) {
+ return 0;
+ }
+
+ return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ()));
+ }
+
+ T & operator=(T const& v) {
this->set (v);
return this->_current;
}
-
-private:
- /* note that we do not set a locale for the streams used
- in to_string() or from_string(), because we want the
- format to be portable across locales (i.e. C or
- POSIX). Also, there is the small matter of
- std::locale aborting on OS X if used with anything
- other than C or POSIX locales.
- */
- std::string to_string (T const & v) const {
+
+private:
+ friend class PropertyFactory;
+
+ /* no copy-construction */
+ Property (Property<T> const &);
+
+ /* Note that we do not set a locale for the streams used
+ * in to_string() or from_string(), because we want the
+ * format to be portable across locales (i.e. C or
+ * POSIX). Also, there is the small matter of
+ * std::locale aborting on OS X if used with anything
+ * other than C or POSIX locales.
+ */
+ virtual std::string to_string (T const& v) const {
std::stringstream s;
s.precision (12); // in case its floating point
s << v;
return s.str ();
}
-
- T from_string (std::string const & s) const {
+
+ virtual T from_string (std::string const& s) const {
std::stringstream t (s);
- T v;
+ T v;
t >> v;
return v;
}
+
};
/** Specialization, for std::string which is common and special (see to_string() and from_string()
- Using stringstream to read from a std::string is easy to get wrong because of whitespace
- delineation, etc.
+ * Using stringstream to read from a std::string is easy to get wrong because of whitespace
+ * separators, etc.
*/
-template <>
-class Property<std::string> : public PropertyTemplate<std::string>
+template<>
+class /*LIBPBD_API*/ Property<std::string> : public PropertyTemplate<std::string>
{
public:
- Property (PropertyDescriptor<std::string> q, std::string const & v)
- : PropertyTemplate<std::string> (q, v)
- {
+ Property (PropertyDescriptor<std::string> d, std::string const & v)
+ : PropertyTemplate<std::string> (d, v)
+ {}
+
+ Property (PropertyDescriptor<std::string> d, std::string const & o, std::string const & c)
+ : PropertyTemplate<std::string> (d, o, c)
+ {}
+ Property<std::string>* clone () const {
+ return new Property<std::string> (this->property_id(), _old, _current);
}
- std::string & operator= (std::string const & v) {
+ std::string & operator= (std::string const& v) {
this->set (v);
return this->_current;
}
-
-private:
- std::string to_string (std::string const & v) const {
- return _current;
+
+private:
+ std::string to_string (std::string const& v) const {
+ return v;
}
-
- std::string from_string (std::string const & s) const {
+
+ std::string from_string (std::string const& s) const {
return s;
}
+
+ /* no copy-construction */
+ Property (Property<std::string> const &);
};
-class PropertyList : public std::map<PropertyID,PropertyBase*>
+template<class T>
+class /*LIBPBD_API*/ EnumProperty : public Property<T>
{
public:
- PropertyList() : property_owner (true) {}
- virtual ~PropertyList() {
- if (property_owner)
- for (std::map<PropertyID,PropertyBase*>::iterator i = begin(); i != end(); ++i) {
- delete i->second;
- }
- }
- /* classes that own property lists use this to add their
- property members to their plists.
- */
- bool add (PropertyBase& p) {
- return insert (value_type (p.id(), &p)).second;
- }
-
- /* code that is constructing a property list for use
- in setting the state of an object uses this.
- */
- template<typename T, typename V> bool add (PropertyDescriptor<T> pid, const V& v) {
- return insert (value_type (pid.id, new Property<T> (pid, (T) v))).second;
- }
+ EnumProperty (PropertyDescriptor<T> q, T const& v)
+ : Property<T> (q, v)
+ {}
-protected:
- bool property_owner;
-};
+ T & operator=(T const& v) {
+ this->set (v);
+ return this->_current;
+ }
-/** A variant of PropertyList that does not delete its
- property list in its destructor. Objects with their
- own Properties store them in an OwnedPropertyList
- to avoid having them deleted at the wrong time.
-*/
+private:
+ std::string to_string (T const & v) const {
+ return enum_2_string (v);
+ }
-class OwnedPropertyList : public PropertyList
+ T from_string (std::string const & s) const {
+ return static_cast<T> (string_2_enum (s, this->_current));
+ }
+
+ /* no copy-construction */
+ EnumProperty (EnumProperty const &);
+};
+
+/** A Property which holds a shared_ptr to a Stateful object,
+ * and handles undo using the somewhat inefficient approach
+ * of saving the complete XML state of its object before and
+ * after changes. A sort of half-way house between the old
+ * complete-state undo system and the new difference-based
+ * one.
+ */
+template <class T>
+class /*LIBPBD_API*/ SharedStatefulProperty : public PropertyBase
{
public:
- OwnedPropertyList() { property_owner = false; }
+ typedef boost::shared_ptr<T> Ptr;
+
+ SharedStatefulProperty (PropertyID d, Ptr p)
+ : PropertyBase (d)
+ , _current (p)
+ {
+
+ }
+
+ SharedStatefulProperty (PropertyID d, Ptr o, Ptr c)
+ : PropertyBase (d)
+ , _old (o)
+ , _current (c)
+ {
+
+ }
+
+ bool set_value (XMLNode const & node) {
+
+ /* Look for our node */
+ XMLNode* n = node.child (property_name ());
+ if (!n) {
+ return false;
+ }
+
+ /* And there should be one child which is the state of our T */
+ XMLNodeList const & children = n->children ();
+ if (children.size() != 1) {
+ return false;
+ }
+
+ _current->set_state (*children.front (), Stateful::current_state_version);
+ return true;
+ }
+
+ void get_value (XMLNode & node) const {
+ XMLNode* n = node.add_child (property_name ());
+ n->add_child_nocopy (_current->get_state ());
+ }
+
+ void clear_changes () {
+ /* We are starting to change things, so _old gets set up
+ with the current state.
+ */
+ _old.reset (new T (*_current.get()));
+ }
+
+ bool changed () const {
+ /* Expensive, but, hey; this requires operator!= in
+ our T
+ */
+ return (*_old != *_current);
+ }
+
+ void invert () {
+ _current.swap (_old);
+ }
+
+ void get_changes_as_xml (XMLNode* history_node) const {
+ /* We express the diff as before and after state, just
+ as MementoCommand does.
+ */
+ XMLNode* p = history_node->add_child (property_name ());
+ XMLNode* from = p->add_child ("from");
+ from->add_child_nocopy (_old->get_state ());
+ XMLNode* to = p->add_child ("to");
+ to->add_child_nocopy (_current->get_state ());
+ }
+
+ void get_changes_as_properties (PropertyList& changes, Command *) const {
+ if (changed ()) {
+ changes.add (clone ());
+ }
+ }
+
+ void apply_changes (PropertyBase const * p) {
+ *_current = *(dynamic_cast<SharedStatefulProperty const *> (p))->val ();
+ }
+
+ Ptr val () const {
+ return _current;
+ }
+
+ T* operator-> () const {
+ return _current.operator-> ();
+ }
+
+ operator bool () const {
+ return _current ? true : false;
+ }
+
+protected:
+
+ Ptr _old;
+ Ptr _current;
+
+private:
+
+ /* No copy-construction nor assignment */
+ SharedStatefulProperty (SharedStatefulProperty<T> const &);
+ SharedStatefulProperty<T>& operator= (SharedStatefulProperty<T> const &);
};
-
+
} /* namespace PBD */
+#include "pbd/property_list_impl.h"
+#include "pbd/property_basics_impl.h"
+
#endif /* __pbd_properties_h__ */