1 #ifndef __libpbd_sequence_property_h__
2 #define __libpbd_sequence_property_h__
9 #include <boost/function.hpp>
11 #include "pbd/convert.h"
13 #include "pbd/property_basics.h"
18 template<typename Container>
19 class SequenceProperty : public PropertyBase
22 typedef std::set<typename Container::value_type> ChangeContainer;
25 ChangeContainer added;
26 ChangeContainer removed;
29 SequenceProperty (PropertyID id, const boost::function<void(const ChangeRecord&)>& update)
30 : PropertyBase (id), _update_callback (update) {}
32 virtual typename Container::value_type lookup_id (const PBD::ID&) = 0;
34 void invert_changes () {
36 /* reverse the adds/removes so that this property's change member
37 correctly describes how to undo the changes it currently
38 reflects. A derived instance of this type of property will
39 create a pdiff() pair by copying the property twice, and
40 calling this method on the "before" item of the pair.
43 _change.removed.swap (_change.added);
46 void add_history_state (XMLNode* history_node) const {
48 XMLNode* child = new XMLNode (PBD::capitalize (property_name()));
49 history_node->add_child_nocopy (*child);
51 /* record the change described in our change member */
53 if (!_change.added.empty()) {
54 for (typename ChangeContainer::iterator i = _change.added.begin(); i != _change.added.end(); ++i) {
55 XMLNode* add_node = new XMLNode (X_("Add"));
56 child->add_child_nocopy (*add_node);
57 add_node->add_property (X_("id"), (*i)->id().to_s());
60 if (!_change.removed.empty()) {
61 for (typename ChangeContainer::iterator i = _change.removed.begin(); i != _change.removed.end(); ++i) {
62 XMLNode* remove_node = new XMLNode (X_("Remove"));
63 child->add_child_nocopy (*remove_node);
64 remove_node->add_property (X_("id"), (*i)->id().to_s());
69 bool set_state_from_owner_state (XMLNode const& owner_state) {
71 XMLProperty const* n = owner_state.property (X_("name"));
77 assert (g_quark_from_string (n->value().c_str()) == property_id());
79 const XMLNodeList& children = owner_state.children();
81 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
83 if ((*c)->name() == X_("Added")) {
84 const XMLNodeList& grandchildren = (*c)->children();
85 for (XMLNodeList::const_iterator gc = grandchildren.begin(); gc != grandchildren.end(); ++gc) {
86 const XMLProperty* prop = (*gc)->property (X_("id"));
88 typename Container::value_type v = lookup_id (PBD::ID (prop->value()));
90 _change.added.insert (v);
94 } else if ((*c)->name() == X_("Removed")) {
95 const XMLNodeList& grandchildren = (*c)->children();
96 for (XMLNodeList::const_iterator gc = grandchildren.begin(); gc != grandchildren.end(); ++gc) {
97 const XMLProperty* prop = (*gc)->property (X_("id"));
99 typename Container::value_type v = lookup_id (PBD::ID (prop->value()));
101 _change.removed.insert (v);
111 void add_state_to_owner_state (XMLNode& owner_state_node) const {
112 for (typename Container::const_iterator i = _val.begin(); i != _val.end(); ++i) {
113 owner_state_node.add_child_nocopy ((*i)->get_state ());
117 bool changed () const {
118 return !_change.added.empty() || !_change.removed.empty();
121 void clear_history () {
122 _change.added.clear ();
123 _change.removed.clear ();
126 void set_state_from_property (PropertyBase const * p) {
127 const ChangeRecord& change (dynamic_cast<const SequenceProperty*> (p)->change ());
131 /** Given a record of changes to this property, pass it to a callback that will
132 * update the property in some appropriate way.
134 * This exists because simply using std::sequence methods to add/remove items
135 * from the property is far too simplistic - the semantics of add/remove may
136 * be much more complex than that.
138 void update (const ChangeRecord& cr) {
139 _update_callback (cr);
142 /* Wrap salient methods of Sequence
145 typename Container::iterator begin() { return _val.begin(); }
146 typename Container::iterator end() { return _val.end(); }
147 typename Container::const_iterator begin() const { return _val.begin(); }
148 typename Container::const_iterator end() const { return _val.end(); }
150 typename Container::reverse_iterator rbegin() { return _val.rbegin(); }
151 typename Container::reverse_iterator rend() { return _val.rend(); }
152 typename Container::const_reverse_iterator rbegin() const { return _val.rbegin(); }
153 typename Container::const_reverse_iterator rend() const { return _val.rend(); }
155 typename Container::iterator insert (typename Container::iterator i, const typename Container::value_type& v) {
156 _change.added.insert (v);
157 return _val.insert (i, v);
160 typename Container::iterator erase (typename Container::iterator i) {
161 if (i != _val.end()) {
162 _change.removed.insert (*i);
164 return _val.erase (i);
167 typename Container::iterator erase (typename Container::iterator f, typename Container::iterator l) {
168 for (typename Container::const_iterator i = f; i != l; ++i) {
169 _change.removed.insert(*i);
171 return _val.erase (f, l);
174 void push_back (const typename Container::value_type& v) {
175 _change.added.insert (v);
179 void push_front (const typename Container::value_type& v) {
180 _change.added.insert (v);
186 _change.removed.insert (front());
193 _change.removed.insert (front());
199 _change.removed.insert (_val.begin(), _val.end());
203 typename Container::size_type size() const {
211 Container& operator= (const Container& other) {
212 _change.removed.insert (_val.begin(), _val.end());
213 _change.added.insert (other.begin(), other.end());
217 typename Container::reference front() {
218 return _val.front ();
221 typename Container::const_reference front() const {
222 return _val.front ();
225 typename Container::reference back() {
229 typename Container::const_reference back() const {
237 template<class BinaryPredicate> void sort(BinaryPredicate comp) {
241 const ChangeRecord& change() const { return _change; }
243 /* for use in building up a SequenceProperty from a serialized
247 void record_addition (typename Container::value_type v) {
248 _change.added.insert (v);
250 void record_removal (typename Container::value_type v) {
251 _change.added.erase (v);
256 ChangeRecord _change;
257 boost::function<void(const ChangeRecord&)> _update_callback;
259 /** Load serialized change history.
260 * @return true if loading succeeded, false otherwise
263 bool load_history_state (const XMLNode& history_node) {
265 const XMLNodeList& children (history_node.children());
267 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
268 const XMLProperty* prop = (*i)->property ("id");
270 PBD::ID id (prop->value());
271 typename Container::value_type v = lookup_id (id);
273 std::cerr << "No such item, ID = " << id.to_s() << " (from " << prop->value() << ")\n";
276 if ((*i)->name() == "Add") {
277 _change.added.insert (v);
278 } else if ((*i)->name() == "Remove") {
279 _change.removed.insert (v);
290 #endif /* __libpbd_sequence_property_h__ */