X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fpbd%2Fsequence_property.h;h=b9d59724ddc56175fb6d9dab62ec1ea1ae352cb5;hb=0aac62e013e15e380001dafae39d554f8765a4a1;hp=e92aa5c68e89126bf895a7ac65be3c605981173d;hpb=fde848282d43db6f7b337a959b5268236d377404;p=ardour.git diff --git a/libs/pbd/pbd/sequence_property.h b/libs/pbd/pbd/sequence_property.h index e92aa5c68e..b9d59724dd 100644 --- a/libs/pbd/pbd/sequence_property.h +++ b/libs/pbd/pbd/sequence_property.h @@ -31,6 +31,8 @@ #include "pbd/id.h" #include "pbd/property_basics.h" #include "pbd/property_list.h" +#include "pbd/stateful_diff_command.h" +#include "pbd/error.h" namespace PBD { @@ -79,44 +81,41 @@ class SequenceProperty : public PropertyBase SequenceProperty (PropertyID id, const boost::function& 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 diff() pair by copying the property twice, and - calling this method on the "before" item of the pair. - */ - _change.removed.swap (_change.added); + void invert () { + _changes.removed.swap (_changes.added); } - void get_change (XMLNode* history_node) const { + void get_changes_as_xml (XMLNode* history_node) const { XMLNode* child = new XMLNode (PBD::capitalize (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) { + if (!_changes.added.empty()) { + for (typename ChangeContainer::iterator i = _changes.added.begin(); i != _changes.added.end(); ++i) { XMLNode* add_node = new XMLNode ("Add"); child->add_child_nocopy (*add_node); - add_node->add_property ("id", (*i)->id().to_s()); + get_content_as_xml (*i, *add_node); } } - if (!_change.removed.empty()) { - for (typename ChangeContainer::iterator i = _change.removed.begin(); i != _change.removed.end(); ++i) { + if (!_changes.removed.empty()) { + for (typename ChangeContainer::iterator i = _changes.removed.begin(); i != _changes.removed.end(); ++i) { XMLNode* remove_node = new XMLNode ("Remove"); child->add_child_nocopy (*remove_node); - remove_node->add_property ("id", (*i)->id().to_s()); + get_content_as_xml (*i, *remove_node); } } } + /** Get a representation of one of our items as XML. The representation must be sufficient to + * restore the item's state later; an ID is ok if someone else is storing the item state, + * otherwise it needs to be the full state. The supplied node is an \ or \ + * which this method can either add properties or children to. + */ + virtual void get_content_as_xml (typename ChangeContainer::value_type, XMLNode &) const = 0; + bool set_value (XMLNode const &) { /* XXX: not used, but probably should be */ assert (false); @@ -130,16 +129,16 @@ class SequenceProperty : public PropertyBase } bool changed () const { - return !_change.added.empty() || !_change.removed.empty(); + return !_changes.added.empty() || !_changes.removed.empty(); } - void clear_history () { - _change.added.clear (); - _change.removed.clear (); + void clear_changes () { + _changes.added.clear (); + _changes.removed.clear (); } - void apply_change (PropertyBase const * p) { - const ChangeRecord& change (dynamic_cast (p)->change ()); + void apply_changes (PropertyBase const * p) { + const ChangeRecord& change (dynamic_cast (p)->changes ()); update (change); } @@ -154,60 +153,76 @@ class SequenceProperty : public PropertyBase _update_callback (cr); } - void diff (PBD::PropertyList& undo, PBD::PropertyList& redo, Command* cmd) const { - if (changed ()) { - /* list of the removed/added items since clear_history() was last called */ - SequenceProperty* a = copy_for_history (); - - /* the same list, but with removed/added lists swapped (for undo purposes) */ - SequenceProperty* b = copy_for_history (); - b->invert_changes (); - - if (cmd) { - /* whenever one of the items emits DropReferences, make sure - that the Destructible we've been told to notify hears about - it. the Destructible is likely to be the Command being built - with this diff(). - */ + void get_changes_as_properties (PBD::PropertyList& changes, Command* cmd) const { + if (!changed ()) { + return; + } + + /* Create a property with just the changes and not the actual values */ + SequenceProperty* a = create (); + a->_changes = _changes; + changes.add (a); + + if (cmd) { + /* whenever one of the items emits DropReferences, make sure + that the Destructible we've been told to notify hears about + it. the Destructible is likely to be the Command being built + with this diff(). + */ - for (typename ChangeContainer::iterator i = a->change().added.begin(); i != a->change().added.end(); ++i) { - (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd)); - } - } - - undo.add (b); - redo.add (a); + for (typename ChangeContainer::iterator i = a->changes().added.begin(); i != a->changes().added.end(); ++i) { + (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd)); + } } } - SequenceProperty* maybe_clone_self_if_found_in_history_node (XMLNode const & node) const { + SequenceProperty* clone_from_xml (XMLNode const & node) const { XMLNodeList const children = node.children (); + + /* find the node for this property name */ - for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { - - if ((*i)->name() == capitalize (property_name())) { - - SequenceProperty* p = create (); - - if (p->set_change (**i)) { - return p; - } else { - delete p; - } + std::string const c = capitalize (property_name ()); + XMLNodeList::const_iterator i = children.begin(); + while (i != children.end() && (*i)->name() != c) { + ++i; + } + + if (i == children.end()) { + return 0; + } + + /* create a property with the changes */ + + SequenceProperty* p = create (); + + XMLNodeList const & grandchildren = (*i)->children (); + for (XMLNodeList::const_iterator j = grandchildren.begin(); j != grandchildren.end(); ++j) { + + typename Container::value_type v = get_content_from_xml (**j); + + if (!v) { + warning << "undo transaction references an unknown object" << endmsg; + } else if ((*j)->name() == "Add") { + p->_changes.added.insert (v); + } else if ((*j)->name() == "Remove") { + p->_changes.removed.insert (v); } - } + } - return 0; + return p; } - void clear_owned_history () { + /** Given an \ or \ node as passed into get_content_to_xml, obtain an item */ + virtual typename Container::value_type get_content_from_xml (XMLNode const & node) const = 0; + + void clear_owned_changes () { for (typename Container::iterator i = begin(); i != end(); ++i) { - (*i)->clear_history (); + (*i)->clear_changes (); } } - void rdiff (std::vector& cmds) const { + void rdiff (std::vector& cmds) const { for (typename Container::const_iterator i = begin(); i != end(); ++i) { if ((*i)->changed ()) { StatefulDiffCommand* sdc = new StatefulDiffCommand (*i); @@ -216,9 +231,7 @@ class SequenceProperty : public PropertyBase } } - - - Container rlist() { return _val; } + Container rlist() const { return _val; } /* Wrap salient methods of Sequence */ @@ -234,51 +247,56 @@ class SequenceProperty : public PropertyBase typename Container::const_reverse_iterator rend() const { return _val.rend(); } typename Container::iterator insert (typename Container::iterator i, const typename Container::value_type& v) { - _change.add (v); + _changes.add (v); return _val.insert (i, v); } typename Container::iterator erase (typename Container::iterator i) { if (i != _val.end()) { - _change.remove (*i); + _changes.remove (*i); } return _val.erase (i); } typename Container::iterator erase (typename Container::iterator f, typename Container::iterator l) { for (typename Container::const_iterator i = f; i != l; ++i) { - _change.remove (*i); + _changes.remove (*i); } return _val.erase (f, l); } + void remove (const typename Container::value_type& v) { + _changes.remove (v); + _val.remove (v); + } + void push_back (const typename Container::value_type& v) { - _change.add (v); + _changes.add (v); _val.push_back (v); } void push_front (const typename Container::value_type& v) { - _change.add (v); + _changes.add (v); _val.push_front (v); } void pop_front () { if (!_val.empty()) { - _change.remove (front()); + _changes.remove (front()); } _val.pop_front (); } void pop_back () { if (!_val.empty()) { - _change.remove (back()); + _changes.remove (back()); } _val.pop_back (); } void clear () { for (typename Container::iterator i = _val.begin(); i != _val.end(); ++i) { - _change.remove (*i); + _changes.remove (*i); } _val.clear (); } @@ -293,10 +311,10 @@ class SequenceProperty : public PropertyBase Container& operator= (const Container& other) { for (typename Container::iterator i = _val.begin(); i != _val.end(); ++i) { - _change.remove (*i); + _changes.remove (*i); } for (typename Container::iterator i = other.begin(); i != other.end(); ++i) { - _change.add (*i); + _changes.add (*i); } return _val = other; } @@ -325,56 +343,24 @@ class SequenceProperty : public PropertyBase _val.sort (comp); } - const ChangeRecord& change() const { return _change; } + const ChangeRecord& changes () const { return _changes; } - protected: - Container _val; - ChangeRecord _change; - boost::function _update_callback; - - /** Load serialized change history. - * @return true if loading succeeded, false otherwise - */ - - bool set_change (XMLNode const & 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); - } - } - } +protected: - return true; - } + /* copy construction only by subclasses */ + SequenceProperty (SequenceProperty const & p) + : PropertyBase (p) + , _val (p._val) + , _changes (p._changes) + , _update_callback (p._update_callback) + {} + + Container _val; ///< our actual container of things + ChangeRecord _changes; ///< changes to the container (adds/removes) that have happened since clear_changes() was last called + boost::function _update_callback; -private: +private: virtual SequenceProperty* create () const = 0; - - /* create a copy of this ListSequenceProperty that only - has what is needed for use in a history list command. This - means that it won't contain the actual item list but - will have the added/removed list. - */ - - SequenceProperty* copy_for_history () const { - SequenceProperty* copy = create (); - /* this is all we need */ - copy->_change = _change; - return copy; - } }; }