X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fpbd%2Fproperties.h;h=70d18db4c7bd74468250b7fefe570bc324593bda;hb=59076a7e4c66db12bbbfbf01f012ca2f6ba4bf56;hp=1c3519766ff7a0e32bef4fb400ad329667fe45b6;hpb=5fe37dbc53a7f54d81ff618eaa2fe050a34aeb3b;p=ardour.git diff --git a/libs/pbd/pbd/properties.h b/libs/pbd/pbd/properties.h index 1c3519766f..70d18db4c7 100644 --- a/libs/pbd/pbd/properties.h +++ b/libs/pbd/pbd/properties.h @@ -24,117 +24,58 @@ #include #include #include -#include +#include +#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 -struct PropertyDescriptor { - PropertyID id; - typedef T value_type; -}; - -class PropertyChange : public std::set -{ -public: - PropertyChange() {} - - template - PropertyChange(PropertyDescriptor p) { insert (p.id); } - - PropertyChange(const PropertyChange& other) : std::set (other) {} - - PropertyChange operator=(const PropertyChange& other) { - clear (); - insert (other.begin (), other.end ()); - return *this; - } - - template - PropertyChange operator=(PropertyDescriptor p) { - clear (); - insert (p.id); - return *this; - } - - template - bool contains (PropertyDescriptor 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 - void add (PropertyDescriptor 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 /*LIBPBD_API*/ PropertyTemplate : public PropertyBase { public: - PropertyBase (PropertyID pid) - : _property_id (pid) + PropertyTemplate (PropertyDescriptor p, T const& v) + : PropertyBase (p.property_id) , _have_old (false) + , _current (v) {} - /** Forget about any old value for this state */ - void clear_history () { - _have_old = false; - } - - 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; } - - bool operator==(PropertyID pid) const { - return _property_id == pid; - } - -protected: - PropertyID _property_id; - bool _have_old; -}; + PropertyTemplate (PropertyDescriptor p, T const& o, T const& c) + : PropertyBase (p.property_id) + , _have_old (true) + , _current (c) + , _old (o) + {} -/** Parent class for classes which represent a single property in a Stateful object */ -template -class PropertyTemplate : public PropertyBase -{ -public: - PropertyTemplate (PropertyDescriptor p, T const& v) - : PropertyBase (p.id) - , _current (v) + PropertyTemplate (PropertyDescriptor p, PropertyTemplate const & s) + : PropertyBase (p.property_id) + , _have_old (false) + , _current (s._current) {} - PropertyTemplate& operator=(PropertyTemplate 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; - return *this; - } + /* OPERATORS / ACCESSORS */ T & operator=(T const& v) { set (v); return _current; } + /* 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 & operator= (PropertyTemplate const & p) { + set (p._current); + return *this; + } + T & operator+=(T const& v) { set (_current + v); return _current; @@ -156,25 +97,12 @@ public: 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); - } - } + /* MANAGEMENT OF Stateful State */ + + bool set_value (XMLNode const & node) { - /** 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 ()); + XMLProperty const* p = node.property (property_name()); if (p) { T const v = from_string (p->value ()); @@ -188,47 +116,147 @@ public: 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* > (p)->val (); + if (v != _current) { + set (v); + } } protected: + void set (T const& v) { - _old = _current; - _have_old = true; - _current = 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; + 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 const &); }; -template +template /*LIBPBD_API*/ std::ostream & operator<<(std::ostream& os, PropertyTemplate const& s) { 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 Property : public PropertyTemplate +class /*LIBPBD_API*/ Property : public PropertyTemplate { public: Property (PropertyDescriptor q, T const& v) : PropertyTemplate (q, v) {} + Property (PropertyDescriptor q, T const& o, T const& c) + : PropertyTemplate (q, o, c) + {} + + Property (PropertyDescriptor q, Property const& v) + : PropertyTemplate (q, v) + {} + + Property* clone () const { + return new Property (this->property_id(), this->_old, this->_current); + } + + Property* 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 (this->property_id(), from_string (from->value()), from_string (to->value ())); + } + T & operator=(T const& v) { this->set (v); return this->_current; } private: + friend class PropertyFactory; + + /* no copy-construction */ + Property (Property 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 @@ -236,14 +264,14 @@ private: * std::locale aborting on OS X if used with anything * other than C or POSIX locales. */ - std::string to_string (T const& v) const { + 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; @@ -254,74 +282,185 @@ private: /** 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. + * separators, etc. */ template<> -class Property : public PropertyTemplate +class /*LIBPBD_API*/ Property : public PropertyTemplate { public: - Property (PropertyDescriptor q, std::string const& v) - : PropertyTemplate (q, v) + Property (PropertyDescriptor d, std::string const & v) + : PropertyTemplate (d, v) + {} + + Property (PropertyDescriptor d, std::string const & o, std::string const & c) + : PropertyTemplate (d, o, c) {} + + Property* clone () const { + return new Property (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; + return v; } std::string from_string (std::string const& s) const { return s; } + /* no copy-construction */ + Property (Property const &); }; -class PropertyList : public std::map +template +class /*LIBPBD_API*/ EnumProperty : public Property { public: - PropertyList() : _property_owner (true) {} - virtual ~PropertyList() { - if (_property_owner) { - for (std::map::iterator i = begin (); i != end (); ++i) { - delete i->second; - } - } + EnumProperty (PropertyDescriptor q, T const& v) + : Property (q, v) + {} + + T & operator=(T const& v) { + this->set (v); + return this->_current; } - /* 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; +private: + std::string to_string (T const & v) const { + return enum_2_string (v); } - /* Code that is constructing a property list for use - * in setting the state of an object uses this. - */ - template - bool add (PropertyDescriptor pid, const V& v) { - return insert (value_type (pid.id, new Property (pid, (T)v))).second; + T from_string (std::string const & s) const { + return static_cast (string_2_enum (s, this->_current)); } -protected: - bool _property_owner; + /* no copy-construction */ + EnumProperty (EnumProperty const &); }; -/** 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. +/** 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. */ -class OwnedPropertyList : public PropertyList +template +class /*LIBPBD_API*/ SharedStatefulProperty : public PropertyBase { public: - OwnedPropertyList() { _property_owner = false; } + typedef boost::shared_ptr 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 (p))->val (); + } + + Ptr val () const { + return _current; + } + + T* operator-> () const { + return _current.operator-> (); + } + + operator bool () const { + return _current; + } + +protected: + + Ptr _old; + Ptr _current; + +private: + + /* No copy-construction nor assignment */ + SharedStatefulProperty (SharedStatefulProperty const &); + SharedStatefulProperty& operator= (SharedStatefulProperty const &); }; } /* namespace PBD */ +#include "pbd/property_list_impl.h" +#include "pbd/property_basics_impl.h" + #endif /* __pbd_properties_h__ */