diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2010-03-02 00:00:00 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2010-03-02 00:00:00 +0000 |
commit | db8b575c30845bafc34b87bacd52129c95d1c478 (patch) | |
tree | 7a521b7795cc6cc4e41d717a0feabd1aefb83e1f /libs/pbd | |
parent | 3540594dc53137eb9e857f9e3c1309382a6d7bef (diff) |
the mega-properties/SequenceProperty patch. split is broken at present (right hand starts has start-in-source of zero)
git-svn-id: svn://localhost/ardour2/branches/3.0@6718 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/pbd')
-rw-r--r-- | libs/pbd/debug.cc | 108 | ||||
-rw-r--r-- | libs/pbd/pbd/debug.h | 57 | ||||
-rw-r--r-- | libs/pbd/pbd/properties.h | 199 | ||||
-rw-r--r-- | libs/pbd/pbd/property_basics.h | 116 | ||||
-rw-r--r-- | libs/pbd/pbd/property_basics_impl.h | 53 | ||||
-rw-r--r-- | libs/pbd/pbd/property_list.h | 75 | ||||
-rw-r--r-- | libs/pbd/pbd/property_list_impl.h | 37 | ||||
-rw-r--r-- | libs/pbd/pbd/sequence_property.h | 291 | ||||
-rw-r--r-- | libs/pbd/pbd/stateful.h | 21 | ||||
-rw-r--r-- | libs/pbd/pbd/stateful_diff_command.h | 5 | ||||
-rw-r--r-- | libs/pbd/property_factory.cc | 53 | ||||
-rw-r--r-- | libs/pbd/property_list.cc | 69 | ||||
-rw-r--r-- | libs/pbd/stateful.cc | 78 | ||||
-rw-r--r-- | libs/pbd/stateful_diff_command.cc | 53 | ||||
-rw-r--r-- | libs/pbd/undo.cc | 1 | ||||
-rw-r--r-- | libs/pbd/wscript | 3 |
16 files changed, 1015 insertions, 204 deletions
diff --git a/libs/pbd/debug.cc b/libs/pbd/debug.cc new file mode 100644 index 0000000000..f8abdcb488 --- /dev/null +++ b/libs/pbd/debug.cc @@ -0,0 +1,108 @@ +/* + Copyright (C) 2009 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <cstring> +#include <cstdlib> +#include <iostream> +#include <map> + +#include "pbd/debug.h" + +#include "i18n.h" + +using namespace std; +static uint64_t _debug_bit = 1; +static std::map<const char*,uint64_t> _debug_bit_map; + +uint64_t PBD::DEBUG::Stateful = PBD::new_debug_bit ("stateful"); +uint64_t PBD::DEBUG::Properties = PBD::new_debug_bit ("properties"); + +uint64_t PBD::debug_bits = 0x0; + +uint64_t +PBD::new_debug_bit (const char* name) +{ + uint64_t ret; + _debug_bit_map.insert (make_pair (name, _debug_bit)); + cerr << "debug name " << name << " = " << _debug_bit << endl; + ret = _debug_bit; + _debug_bit <<= 1; + return ret; +} + +void +PBD::debug_print (const char* prefix, string str) +{ + cerr << prefix << ": " << str; +} + +void +PBD::set_debug_bits (uint64_t bits) +{ + debug_bits = bits; +} + +int +PBD::parse_debug_options (const char* str) +{ + char* p; + char* sp; + uint64_t bits = 0; + char* copy = strdup (str); + + p = strtok_r (copy, ",", &sp); + + while (p) { + if (strcasecmp (p, "list") == 0) { + list_debug_options (); + free (copy); + return 1; + } + + if (strcasecmp (p, "all") == 0) { + PBD::set_debug_bits (~0ULL); + free (copy); + return 0; + } + + for (map<const char*,uint64_t>::iterator i = _debug_bit_map.begin(); i != _debug_bit_map.end(); ++i) { + if (strncasecmp (p, i->first, strlen (p)) == 0) { + cerr << "debug args matched for " << p << " set bit " << i->second << endl; + bits |= i->second; + } + } + + p = strtok_r (0, ",", &sp); + } + + free (copy); + PBD::set_debug_bits (bits); + return 0; +} + +void +PBD::list_debug_options () +{ + cerr << _("The following debug options are available. Separate multipe options with commas.\nNames are case-insensitive and can be abbreviated.") << endl << endl; + cerr << "\tAll" << endl; + + for (map<const char*,uint64_t>::iterator i = _debug_bit_map.begin(); i != _debug_bit_map.end(); ++i) { + cerr << "\t" << i->first << endl; + } +} diff --git a/libs/pbd/pbd/debug.h b/libs/pbd/pbd/debug.h new file mode 100644 index 0000000000..a45a867bd2 --- /dev/null +++ b/libs/pbd/pbd/debug.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 2009 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_debug_h__ +#define __libpbd_debug_h__ + +#include <stdint.h> + +#include <sstream> + +namespace PBD { + + extern uint64_t debug_bits; + uint64_t new_debug_bit (const char* name); + void debug_print (const char* prefix, std::string str); + void set_debug_bits (uint64_t bits); + int parse_debug_options (const char* str); + void list_debug_options (); + + namespace DEBUG { + + /* this namespace is so that we can write DEBUG::bit_name */ + + extern uint64_t Stateful; + extern uint64_t Properties; + } +} + +#ifndef NDEBUG +#define DEBUG_TRACE(bits,str) if ((bits) & PBD::debug_bits) { PBD::debug_print (# bits, str); } +#define DEBUG_STR_DECL(id) std::stringstream __debug_str ## id; +#define DEBUG_STR(id) __debug_str ## id +#define DEBUG_STR_APPEND(id,s) __debug_str ## id << s; +#else +#define DEBUG_TRACE(bits,fmt,...) /*empty*/ +#define DEBUG_STR(a) /* empty */ +#define DEBUG_STR_APPEND(a,b) /* empty */ +#endif + +#endif /* __libpbd_debug_h__ */ + diff --git a/libs/pbd/pbd/properties.h b/libs/pbd/pbd/properties.h index 1c3519766f..274dfffd9c 100644 --- a/libs/pbd/pbd/properties.h +++ b/libs/pbd/pbd/properties.h @@ -27,96 +27,19 @@ #include <glib.h> #include "pbd/xml++.h" +#include "pbd/property_basics.h" +#include "pbd/property_list.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 -{ -public: - PropertyBase (PropertyID pid) - : _property_id (pid) - , _have_old (false) - {} - - /** 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; -}; - -/** Parent class for classes which represent a single property in a Stateful object */ +/** Parent class for classes which represent a single scalar property in a Stateful object + */ template<class T> class PropertyTemplate : public PropertyBase { public: PropertyTemplate (PropertyDescriptor<T> p, T const& v) - : PropertyBase (p.id) + : PropertyBase (p.property_id) , _current (v) {} @@ -156,25 +79,22 @@ 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); - } + /** If this property has been changed since the last clear_history() call + (or its construction), add an (XML) property describing the old value + to the XMLNode @param old and another describing the current value to + the XMLNode @param current. + */ + void add_history_state (XMLNode* history_node) const { + history_node->add_property (property_name(), to_string (_current)); } /** 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 ()); + bool set_state_from_owner_state (XMLNode const& owner_state) { + + XMLProperty const* p = owner_state.property (property_name()); if (p) { T const v = from_string (p->value ()); @@ -188,11 +108,19 @@ public: return false; } - void add_state (XMLNode& node) const { - node.add_property (property_name (), to_string (_current)); + void add_state_to_owner_state (XMLNode& owner_state) const { + owner_state.add_property (property_name(), to_string (_current)); } protected: + /** Constructs a PropertyTemplate with a default + value for _old and _current. + */ + + PropertyTemplate (PropertyDescriptor<T> p) + : PropertyBase (p.property_id) + {} + void set (T const& v) { _old = _current; _have_old = true; @@ -212,7 +140,7 @@ std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> 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 T> @@ -222,6 +150,21 @@ public: Property (PropertyDescriptor<T> q, T const& v) : PropertyTemplate<T> (q, v) {} + + void diff (PropertyList& before, PropertyList& after) const { + if (this->_have_old) { + before.add (new Property<T> (this->property_id(), this->_old)); + after.add (new Property<T> (this->property_id(), this->_current)); + } + } + + Property<T>* maybe_clone_self_if_found_in_history_node (const XMLNode& node) const { + const XMLProperty* prop = node.property (this->property_name()); + if (!prop) { + return 0; + } + return new Property<T> (this->property_id(), from_string (prop->value())); + } T & operator=(T const& v) { this->set (v); @@ -229,6 +172,12 @@ public: } private: + friend class PropertyFactory; + + Property (PropertyDescriptor<T> q) + : PropertyTemplate<T> (q) + {} + /* 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 @@ -254,7 +203,7 @@ 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<std::string> : public PropertyTemplate<std::string> @@ -264,6 +213,13 @@ public: : PropertyTemplate<std::string> (q, v) {} + void diff (PropertyList& before, PropertyList& after) const { + if (this->_have_old) { + before.add (new Property<std::string> (PropertyDescriptor<std::string> (this->property_id()), this->_old)); + after.add (new Property<std::string> (PropertyDescriptor<std::string> (this->property_id()), this->_current)); + } + } + std::string & operator=(std::string const& v) { this->set (v); return this->_current; @@ -280,48 +236,9 @@ private: }; -class PropertyList : public std::map<PropertyID, PropertyBase*> -{ -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; - } - -protected: - bool _property_owner; -}; - -/** 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. - */ -class OwnedPropertyList : public PropertyList -{ -public: - OwnedPropertyList() { _property_owner = false; } -}; - } /* namespace PBD */ +#include "pbd/property_list_impl.h" +#include "pbd/property_basics_impl.h" + #endif /* __pbd_properties_h__ */ diff --git a/libs/pbd/pbd/property_basics.h b/libs/pbd/pbd/property_basics.h new file mode 100644 index 0000000000..c99fee1961 --- /dev/null +++ b/libs/pbd/pbd/property_basics.h @@ -0,0 +1,116 @@ +/* + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_property_basics_h__ +#define __libpbd_property_basics_h__ + +#include <glib.h> +#include <set> + +#include "pbd/xml++.h" + +namespace PBD { + +class PropertyList; +typedef GQuark PropertyID; + +template<typename T> +struct PropertyDescriptor { + PropertyDescriptor () : property_id (0) {} + PropertyDescriptor (PropertyID pid) : property_id (pid) {} + + PropertyID property_id; + typedef T value_type; +}; + +class PropertyChange : public std::set<PropertyID> +{ +public: + PropertyChange() {} + + template<typename T> PropertyChange(PropertyDescriptor<T> p); + + 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); + template<typename T> bool contains (PropertyDescriptor<T> p) const; + + 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) { insert (id); } + void add (const PropertyChange& other) { insert (other.begin (), other.end ()); } + template<typename T> void add (PropertyDescriptor<T> p); +}; + +/** Base (non template) part of Property */ +class PropertyBase +{ +public: + PropertyBase (PropertyID pid) + : _property_id (pid) + , _have_old (false) + {} + + /** Forget about any old value for this state */ + virtual void clear_history () { + _have_old = false; + } + + virtual void add_history_state (XMLNode*) const = 0; + virtual void diff (PropertyList&, PropertyList&) const = 0; + + virtual PropertyBase* maybe_clone_self_if_found_in_history_node (const XMLNode&) const { return 0; } + + virtual bool set_state_from_owner_state (XMLNode const&) = 0; + virtual void add_state_to_owner_state (XMLNode&) const = 0; + + const gchar*property_name () const { return g_quark_to_string (_property_id); } + PropertyID property_id () const { return _property_id; } + + bool operator==(PropertyID pid) const { + return _property_id == pid; + } + +protected: + PropertyID _property_id; + bool _have_old; +}; + +class PropertyFactory +{ + public: + static PropertyBase* create (const XMLNode&); +}; + +} + +#endif /* __libpbd_property_basics_h__ */ diff --git a/libs/pbd/pbd/property_basics_impl.h b/libs/pbd/pbd/property_basics_impl.h new file mode 100644 index 0000000000..c63c5ccc5c --- /dev/null +++ b/libs/pbd/pbd/property_basics_impl.h @@ -0,0 +1,53 @@ +/* + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_property_basics_impl_h__ +#define __libpbd_property_basics_impl_h__ + +namespace PBD { + +template<typename T> +PropertyChange::PropertyChange(PropertyDescriptor<T> p) +{ + insert (p.property_id); +} + +template<typename T> PropertyChange +PropertyChange::operator=(PropertyDescriptor<T> p) +{ + clear (); + insert (p.property_id); + return *this; +} + +template<typename T> bool +PropertyChange::contains (PropertyDescriptor<T> p) const +{ + return find (p.property_id) != end (); +} + +template<typename T> void +PropertyChange::add (PropertyDescriptor<T> p) +{ + insert (p.property_id); +} + +} + +#endif /* __libpbd_property_basics_impl_h__ */ diff --git a/libs/pbd/pbd/property_list.h b/libs/pbd/pbd/property_list.h new file mode 100644 index 0000000000..af9d10b88f --- /dev/null +++ b/libs/pbd/pbd/property_list.h @@ -0,0 +1,75 @@ +/* + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __pbd_property_list_h__ +#define __pbd_property_list_h__ + +#include <map> + +#include "pbd/property_basics.h" + +class XMLNode; + +namespace PBD { +class PropertyList : public std::map<PropertyID, PropertyBase*> +{ +public: + PropertyList(); + + virtual ~PropertyList(); + + void add_history_state (XMLNode* before); + + /** Add a property (of some kind) to the list. Used when + constructing PropertyList's that describe a change/operation. + */ + bool add (PropertyBase* prop); + + /* Code that is constructing a property list for use + * in setting the state of an object uses this. + * + * Defined below, once we have Property<T> + */ + template<typename T, typename V> bool add (PropertyDescriptor<T> pid, const V& v); + +protected: + bool _property_owner; +}; + +/** 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. + */ +class OwnedPropertyList : public PropertyList +{ +public: + OwnedPropertyList(); + + /* Classes that own property lists use this to add their + * property members to their plists. Note that it takes + * a reference argument rather than a pointer like + * one of the add() methods in PropertyList. + */ + bool add (PropertyBase& p); +}; + +} + +#endif /* __pbd_property_list_h__ */ diff --git a/libs/pbd/pbd/property_list_impl.h b/libs/pbd/pbd/property_list_impl.h new file mode 100644 index 0000000000..c5cfae11dc --- /dev/null +++ b/libs/pbd/pbd/property_list_impl.h @@ -0,0 +1,37 @@ +/* + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_property_list_impl_h__ +#define __libpbd_property_list_impl_h__ + +#include "pbd/property_list.h" +#include "pbd/properties.h" + +/* now we can define this ... */ + +namespace PBD { + +template<typename T, typename V> bool +PropertyList::add (PropertyDescriptor<T> pid, const V& v) { + return insert (value_type (pid.property_id, new Property<T> (pid, (T)v))).second; +} + +} + +#endif /* __libpbd_property_list_impl_h__ */ diff --git a/libs/pbd/pbd/sequence_property.h b/libs/pbd/pbd/sequence_property.h new file mode 100644 index 0000000000..887250ab82 --- /dev/null +++ b/libs/pbd/pbd/sequence_property.h @@ -0,0 +1,291 @@ +#ifndef __libpbd_sequence_property_h__ +#define __libpbd_sequence_property_h__ + +#include <iostream> + +#include <set> +#include <list> + +#include <boost/function.hpp> + +#include "pbd/id.h" +#include "pbd/property_basics.h" + +#include "i18n.h" + +namespace PBD { +template<typename Container> +class SequenceProperty : public PropertyBase +{ + public: + typedef std::set<typename Container::value_type> ChangeContainer; + + struct ChangeRecord { + ChangeContainer added; + ChangeContainer removed; + }; + + SequenceProperty (PropertyID id, const boost::function<void(const ChangeRecord&)>& update) + : PropertyBase (id), _update_callback (update) {} + + virtual typename Container::value_type lookup_id (const PBD::ID&) = 0; + + void invert_changes () { + + /* reverse the adds/removes so that this property's change member + correctly describes how to undo the changes it currently + reflects. A derived instance of this type of property will + create a pdiff() pair by copying the property twice, and + calling this method on the "before" item of the pair. + */ + + _change.removed.swap (_change.added); + } + + void add_history_state (XMLNode* history_node) const { + + /* XXX need to capitalize property name */ + XMLNode* child = new XMLNode (property_name()); + history_node->add_child_nocopy (*child); + + /* record the change described in our change member */ + + if (!_change.added.empty()) { + for (typename ChangeContainer::iterator i = _change.added.begin(); i != _change.added.end(); ++i) { + XMLNode* add_node = new XMLNode (X_("Add")); + child->add_child_nocopy (*add_node); + add_node->add_property (X_("id"), (*i)->id().to_s()); + } + } + if (!_change.removed.empty()) { + for (typename ChangeContainer::iterator i = _change.removed.begin(); i != _change.removed.end(); ++i) { + XMLNode* remove_node = new XMLNode (X_("Remove")); + child->add_child_nocopy (*remove_node); + remove_node->add_property (X_("id"), (*i)->id().to_s()); + } + } + } + + bool set_state_from_owner_state (XMLNode const& owner_state) { + + XMLProperty const* n = owner_state.property (X_("name")); + + if (!n) { + return false; + } + + assert (g_quark_from_string (n->value().c_str()) == property_id()); + + const XMLNodeList& children = owner_state.children(); + + for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) { + + if ((*c)->name() == X_("Added")) { + const XMLNodeList& grandchildren = (*c)->children(); + for (XMLNodeList::const_iterator gc = grandchildren.begin(); gc != grandchildren.end(); ++gc) { + const XMLProperty* prop = (*gc)->property (X_("id")); + if (prop) { + typename Container::value_type v = lookup_id (PBD::ID (prop->value())); + if (v) { + _change.added.insert (v); + } + } + } + } else if ((*c)->name() == X_("Removed")) { + const XMLNodeList& grandchildren = (*c)->children(); + for (XMLNodeList::const_iterator gc = grandchildren.begin(); gc != grandchildren.end(); ++gc) { + const XMLProperty* prop = (*gc)->property (X_("id")); + if (prop) { + typename Container::value_type v = lookup_id (PBD::ID (prop->value())); + if (v) { + _change.removed.insert (v); + } + } + } + } + } + + return true; + } + + void add_state_to_owner_state (XMLNode& owner_state_node) const { + for (typename Container::const_iterator i = _val.begin(); i != _val.end(); ++i) { + owner_state_node.add_child_nocopy ((*i)->get_state ()); + } + } + + void clear_history () { + PropertyBase::clear_history(); + _change.added.clear (); + _change.removed.clear (); + } + + /** Given a record of changes to this property, pass it to a callback that will + * update the property in some appropriate way. + * + * This exists because simply using std::sequence methods to add/remove items + * from the property is far too simplistic - the semantics of add/remove may + * be much more complex than that. + */ + void update (const ChangeRecord& cr) { + _update_callback (cr); + } + + /* Wrap salient methods of Sequence + */ + + typename Container::iterator begin() { return _val.begin(); } + typename Container::iterator end() { return _val.end(); } + typename Container::const_iterator begin() const { return _val.begin(); } + typename Container::const_iterator end() const { return _val.end(); } + + typename Container::reverse_iterator rbegin() { return _val.rbegin(); } + typename Container::reverse_iterator rend() { return _val.rend(); } + typename Container::const_reverse_iterator rbegin() const { return _val.rbegin(); } + typename Container::const_reverse_iterator rend() const { return _val.rend(); } + + typename Container::iterator insert (typename Container::iterator i, const typename Container::value_type& v) { + _have_old = true; + _change.added.insert (v); + return _val.insert (i, v); + } + + typename Container::iterator erase (typename Container::iterator i) { + if (i != _val.end()) { + _have_old = true; + _change.removed.insert (*i); + } + return _val.erase (i); + } + + typename Container::iterator erase (typename Container::iterator f, typename Container::iterator l) { + _have_old = true; + for (typename Container::const_iterator i = f; i != l; ++i) { + _change.removed.insert(*i); + } + return _val.erase (f, l); + } + + void push_back (const typename Container::value_type& v) { + _have_old = true; + _change.added.insert (v); + _val.push_back (v); + } + + void push_front (const typename Container::value_type& v) { + _have_old = true; + _change.added.insert (v); + _val.push_front (v); + } + + void pop_front () { + if (!_val.empty()) { + _have_old = true; + _change.removed.insert (front()); + } + _val.pop_front (); + } + + void pop_back () { + if (!_val.empty()) { + _have_old = true; + _change.removed.insert (front()); + } + _val.pop_back (); + } + + void clear () { + _have_old = true; + _change.removed.insert (_val.begin(), _val.end()); + _val.clear (); + } + + typename Container::size_type size() const { + return _val.size(); + } + + bool empty() const { + return _val.empty(); + } + + Container& operator= (const Container& other) { + _have_old = true; + _change.removed.insert (_val.begin(), _val.end()); + _change.added.insert (other.begin(), other.end()); + return _val = other; + } + + typename Container::reference front() { + return _val.front (); + } + + typename Container::const_reference front() const { + return _val.front (); + } + + typename Container::reference back() { + return _val.back (); + } + + typename Container::const_reference back() const { + return _val.back (); + } + + void sort() { + _val.sort (); + } + + template<class BinaryPredicate> void sort(BinaryPredicate comp) { + _val.sort (comp); + } + + const ChangeRecord& change() const { return _change; } + + /* for use in building up a SequenceProperty from a serialized + version on disk. + */ + + void record_addition (typename Container::value_type v) { + _change.added.insert (v); + } + void record_removal (typename Container::value_type v) { + _change.added.erase (v); + } + + protected: + Container _val; + ChangeRecord _change; + boost::function<void(const ChangeRecord&)> _update_callback; + + /** Load serialized change history. + * @return true if loading succeeded, false otherwise + */ + + bool load_history_state (const XMLNode& history_node) { + + const XMLNodeList& children (history_node.children()); + + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + const XMLProperty* prop = (*i)->property ("id"); + if (prop) { + PBD::ID id (prop->value()); + typename Container::value_type v = lookup_id (id); + if (!v) { + std::cerr << "No such item, ID = " << id.to_s() << " (from " << prop->value() << ")\n"; + return false; + } + if ((*i)->name() == "Add") { + _change.added.insert (v); + } else if ((*i)->name() == "Remove") { + _change.removed.insert (v); + } + } + } + + return true; + } +}; + +} + +#endif /* __libpbd_sequence_property_h__ */ diff --git a/libs/pbd/pbd/stateful.h b/libs/pbd/pbd/stateful.h index bc32d7fd9b..78b9eb7b3b 100644 --- a/libs/pbd/pbd/stateful.h +++ b/libs/pbd/pbd/stateful.h @@ -26,7 +26,7 @@ #include "pbd/id.h" #include "pbd/xml++.h" -#include "pbd/properties.h" +#include "pbd/property_basics.h" #include "pbd/signals.h" class XMLNode; @@ -37,6 +37,9 @@ namespace sys { class path; } +class PropertyList; +class OwnedPropertyList; + /** Base class for objects with saveable and undoable state */ class Stateful { public: @@ -51,10 +54,9 @@ class Stateful { virtual bool set_property (const PropertyBase&) { return false; } PropertyChange set_properties (const PropertyList&); + const OwnedPropertyList& properties() const { return *_properties; } - void add_property (PropertyBase& s) { - _properties.add (s); - } + void add_property (PropertyBase& s); /* Extra XML node: so that 3rd parties can attach state to the XMLNode representing the state of this object. @@ -65,9 +67,13 @@ class Stateful { const PBD::ID& id() const { return _id; } + /* history management */ + void clear_history (); - std::pair<XMLNode *, XMLNode*> diff () const; - void changed (PropertyChange&) const; + void diff (PropertyList&, PropertyList&) const; + /* create a property list from an XMLNode + */ + virtual PropertyList* property_factory(const XMLNode&) const { return 0; } /* How stateful's notify of changes to their properties */ @@ -85,7 +91,6 @@ class Stateful { to get basic property setting done. */ PropertyChange set_properties (XMLNode const &); - /* derived classes can implement this to do cross-checking of property values after either a PropertyList or XML @@ -98,7 +103,7 @@ class Stateful { PBD::ID _id; std::string _xml_node_name; ///< name of node to use for this object in XML - OwnedPropertyList _properties; + OwnedPropertyList* _properties; }; } // namespace PBD diff --git a/libs/pbd/pbd/stateful_diff_command.h b/libs/pbd/pbd/stateful_diff_command.h index 43efed445d..cab1ae40cf 100644 --- a/libs/pbd/pbd/stateful_diff_command.h +++ b/libs/pbd/pbd/stateful_diff_command.h @@ -28,6 +28,7 @@ namespace PBD { class Stateful; +class PropertyList; /** A Command which stores its action as the differences between the before and after * state of a Stateful object. @@ -46,8 +47,8 @@ public: private: boost::weak_ptr<Stateful> _object; ///< the object in question - XMLNode* _before; ///< XML node containing the previous values of XML properties which changed - XMLNode* _after; ///< XML node containing the new values of XML properties which changed + PBD::PropertyList* _before; ///< its (partial) state before the command + PBD::PropertyList* _after; ///< its (partial) state after the operation }; }; diff --git a/libs/pbd/property_factory.cc b/libs/pbd/property_factory.cc new file mode 100644 index 0000000000..af0a91a0bf --- /dev/null +++ b/libs/pbd/property_factory.cc @@ -0,0 +1,53 @@ +/* + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <stdint.h> +#include <cstdio> + +#include "pbd/properties.h" +#include "pbd/xml++.h" + +#include "i18n.h" + +using namespace PBD; + +PropertyBase* +PropertyFactory::create (const XMLNode& node) +{ + const XMLProperty* prop_type = node.property (X_("property-type")); + const XMLProperty* prop_id = node.property (X_("id")); + const XMLProperty* prop_val = node.property (X_("val")); + + if (!prop_type || !prop_id || !prop_val) { + return 0; + } + + PropertyID id; + sscanf (prop_id->value().c_str(), "%u", &id); + + if (prop_type->value() == typeid (Property<bool>).name()) { + + PropertyDescriptor<bool> pd (id); + Property<bool>* p = new Property<bool> (pd); + p->set (p->from_string (prop_val->value())); + return p; + } + + return 0; +} diff --git a/libs/pbd/property_list.cc b/libs/pbd/property_list.cc new file mode 100644 index 0000000000..ffe1170dd5 --- /dev/null +++ b/libs/pbd/property_list.cc @@ -0,0 +1,69 @@ +/* + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "pbd/debug.h" +#include "pbd/compose.h" +#include "pbd/property_list.h" +#include "pbd/xml++.h" + +using namespace PBD; + +PropertyList::PropertyList() + : _property_owner (true) +{ +} + +PropertyList::~PropertyList () +{ + if (_property_owner) { + for (iterator i = begin (); i != end (); ++i) { + delete i->second; + } + } +} + +void +PropertyList::add_history_state (XMLNode* history_node) +{ + for (const_iterator i = begin(); i != end(); ++i) { + DEBUG_TRACE (DEBUG::Properties, string_compose ("Add before/after to %1 for %2\n", + history_node->name(), + i->second->property_name())); + i->second->add_history_state (history_node); + } +} + +bool +PropertyList::add (PropertyBase* prop) +{ + return insert (value_type (prop->property_id(), prop)).second; +} + +OwnedPropertyList::OwnedPropertyList () +{ + _property_owner = false; +} + +bool +OwnedPropertyList::add (PropertyBase& p) +{ + return insert (value_type (p.property_id (), &p)).second; +} + + diff --git a/libs/pbd/stateful.cc b/libs/pbd/stateful.cc index 9a1a116128..f2d9f9ee12 100644 --- a/libs/pbd/stateful.cc +++ b/libs/pbd/stateful.cc @@ -20,7 +20,10 @@ #include <unistd.h> +#include "pbd/debug.h" #include "pbd/stateful.h" +#include "pbd/property_list.h" +#include "pbd/properties.h" #include "pbd/destructible.h" #include "pbd/filesystem.h" #include "pbd/xml++.h" @@ -36,6 +39,7 @@ int Stateful::current_state_version = 0; int Stateful::loading_state_version = 0; Stateful::Stateful () + : _properties (new OwnedPropertyList) { _extra_xml = 0; _instant_xml = 0; @@ -43,6 +47,8 @@ Stateful::Stateful () Stateful::~Stateful () { + delete _properties; + // Do not delete _extra_xml. The use of add_child_nocopy() // means it needs to live on indefinately. @@ -153,53 +159,30 @@ Stateful::instant_xml (const string& str, const sys::path& directory_path) void Stateful::clear_history () { - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { + for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) { i->second->clear_history (); } } -/** @return A pair of XMLNodes representing state that has changed since the last time clear_history - * was called on this object; the first is the state before, the second the state after. - * - * It is the caller's responsibility to delete the returned XMLNodes. - */ -pair<XMLNode *, XMLNode *> -Stateful::diff () const +void +Stateful::diff (PropertyList& before, PropertyList& after) const { - XMLNode* old = new XMLNode (_xml_node_name); - XMLNode* current = new XMLNode (_xml_node_name); - - for (OwnedPropertyList::const_iterator i = _properties.begin(); i != _properties.end(); ++i) { - i->second->diff (old, current); + for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) { + i->second->diff (before, after); } - - return make_pair (old, current); } -/** Modifies PropertyChange @param c to indicate what properties have changed since the last - time clear_history was called on this object. Note that not all properties have change - values - if this object has any such Property members, they will never show up in - the value of @param c. Note also that @param c is not cleared by this function. -*/ -void -Stateful::changed (PropertyChange& c) const -{ - for (OwnedPropertyList::const_iterator i = _properties.begin(); i != _properties.end(); ++i) { - i->second->diff (c); - } -} - /** Set state of some/all _properties from an XML node. * @param node Node. * @return PropertyChanges made. */ PropertyChange -Stateful::set_properties (XMLNode const & node) +Stateful::set_properties (XMLNode const & owner_state) { PropertyChange c; - - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { - if (i->second->set_state (node)) { + + for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) { + if (i->second->set_state_from_owner_state (owner_state)) { c.add (i->first); } } @@ -215,12 +198,22 @@ Stateful::set_properties (const PropertyList& property_list) PropertyChange c; PropertyList::const_iterator p; - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { - if ((p = property_list.find (i->first)) != property_list.end()) { - if (set_property (*p->second)) { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("Stateful %1 setting properties from list of %2\n", this, property_list.size())); + + for (PropertyList::const_iterator pp = property_list.begin(); pp != property_list.end(); ++pp) { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("in plist: %1\n", pp->second->property_name())); + } + + for (PropertyList::const_iterator i = property_list.begin(); i != property_list.end(); ++i) { + if ((p = _properties->find (i->first)) != _properties->end()) { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("actually setting property %1\n", p->second->property_name())); + if (set_property (*i->second)) { c.add (i->first); } - } + } else { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("passed in property %1 not found in own property list\n", + i->second->property_name())); + } } post_set (); @@ -228,16 +221,21 @@ Stateful::set_properties (const PropertyList& property_list) return c; } - /** Add property states to an XML node. * @param node Node. */ void -Stateful::add_properties (XMLNode & node) +Stateful::add_properties (XMLNode& owner_state) { - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { - i->second->add_state (node); + for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) { + i->second->add_state_to_owner_state (owner_state); } } +void +Stateful::add_property (PropertyBase& s) +{ + _properties->add (s); +} + } // namespace PBD diff --git a/libs/pbd/stateful_diff_command.cc b/libs/pbd/stateful_diff_command.cc index bf092a6594..0c7b8cc3f2 100644 --- a/libs/pbd/stateful_diff_command.cc +++ b/libs/pbd/stateful_diff_command.cc @@ -17,7 +17,10 @@ */ +#include <iostream> + #include "pbd/stateful_diff_command.h" +#include "pbd/property_list.h" #include "i18n.h" using namespace std; @@ -30,24 +33,35 @@ using namespace PBD; StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<Stateful> s) : _object (s) + , _before (new PropertyList) + , _after (new PropertyList) { - pair<XMLNode *, XMLNode*> const p = s->diff (); - _before = p.first; - _after = p.second; + s->diff (*_before, *_after); } StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<Stateful> s, XMLNode const & n) : _object (s) + , _before (0) + , _after (0) { - _before = new XMLNode (*n.children().front()); - _after = new XMLNode (*n.children().back()); + const XMLNodeList& children (n.children()); + + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + if ((*i)->name() == X_("Undo")) { + _before = s->property_factory (**i); + } else if ((*i)->name() == X_("Do")) { + _after = s->property_factory (**i); + } + } + + assert (_before != 0); + assert (_after != 0); } - StatefulDiffCommand::~StatefulDiffCommand () { - delete _before; - delete _after; + delete _before; + delete _after; } void @@ -56,7 +70,10 @@ StatefulDiffCommand::operator() () boost::shared_ptr<Stateful> s (_object.lock()); if (s) { - s->set_state (*_after, Stateful::current_state_version); + PropertyChange changed = s->set_properties (*_after); + if (!changed.empty()) { + s->PropertyChanged (changed); + } } } @@ -66,7 +83,12 @@ StatefulDiffCommand::undo () boost::shared_ptr<Stateful> s (_object.lock()); if (s) { - s->set_state (*_before, Stateful::current_state_version); + std::cerr << "Undoing a stateful diff command\n"; + PropertyChange changed = s->set_properties (*_before); + if (!changed.empty()) { + std::cerr << "Sending changed\n"; + s->PropertyChanged (changed); + } } } @@ -84,8 +106,15 @@ StatefulDiffCommand::get_state () node->add_property ("obj-id", s->id().to_s()); node->add_property ("type-name", typeid(*s.get()).name()); - node->add_child_copy (*_before); - node->add_child_copy (*_after); + + XMLNode* before = new XMLNode (X_("Undo")); + XMLNode* after = new XMLNode (X_("Do")); + + _before->add_history_state (before); + _after->add_history_state (after); + + node->add_child_nocopy (*before); + node->add_child_nocopy (*after); return *node; } diff --git a/libs/pbd/undo.cc b/libs/pbd/undo.cc index a4042a7e13..a06127926f 100644 --- a/libs/pbd/undo.cc +++ b/libs/pbd/undo.cc @@ -309,7 +309,6 @@ UndoHistory::redo (unsigned int n) timersub (&end, &start, &diff); cerr << "Redo took " << diff.tv_sec << '.' << diff.tv_usec << endl; - EndUndoRedo (); /* EMIT SIGNAL */ Changed (); /* EMIT SIGNAL */ } diff --git a/libs/pbd/wscript b/libs/pbd/wscript index 8bd00bcff9..08c0f2670b 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -59,6 +59,7 @@ def build(bld): controllable.cc controllable_descriptor.cc crossthread.cc + debug.cc enumwriter.cc event_loop.cc dmalloc.cc @@ -74,6 +75,8 @@ def build(bld): mountpoint.cc pathscanner.cc pool.cc + property_factory.cc + property_list.cc pthread_utils.cc receiver.cc search_path.cc |