Modify StatefulDiffCommand undo record to only contain the changes in one direction...
[ardour.git] / libs / pbd / pbd / sequence_property.h
1 /*
2     Copyright (C) 2010 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef __libpbd_sequence_property_h__
21 #define __libpbd_sequence_property_h__
22
23 #include <iostream>
24
25 #include <set>
26 #include <list>
27
28 #include <boost/function.hpp>
29
30 #include "pbd/convert.h"
31 #include "pbd/id.h"
32 #include "pbd/property_basics.h"
33 #include "pbd/property_list.h"
34
35 namespace PBD {
36
37 /** A base class for properties whose state is a container of other
38  *  things.  Its behaviour is `specialised' for this purpose in that
39  *  it holds state changes as additions to and removals from the
40  *  container, which is more efficient than storing entire state after
41  *  any change.
42  */
43 template<typename Container>
44 class SequenceProperty : public PropertyBase
45 {
46   public:
47         typedef std::set<typename Container::value_type> ChangeContainer;
48
49         /** A record of changes made */
50         struct ChangeRecord {
51
52                 void add (typename Container::value_type const & r) {
53                         typename ChangeContainer::iterator i = removed.find (r);
54                         if (i != removed.end()) {
55                                 /* we're adding, and this thing has already been marked as removed, so
56                                    just remove it from the removed list
57                                 */
58                                 removed.erase (r);
59                         } else {
60                                 added.insert (r);
61                         }
62                 }
63
64                 void remove (typename Container::value_type const & r) {
65                         typename ChangeContainer::iterator i = added.find (r);
66                         if (i != added.end()) {
67                                 /* we're removing, and this thing has already been marked as added, so
68                                    just remove it from the added list
69                                 */
70                                 added.erase (i);
71                         } else {
72                                 removed.insert (r);
73                         }
74                 }
75
76                 ChangeContainer added;
77                 ChangeContainer removed;
78         };
79
80         SequenceProperty (PropertyID id, const boost::function<void(const ChangeRecord&)>& update)
81                 : PropertyBase (id), _update_callback (update) {}
82
83         virtual typename Container::value_type lookup_id (const PBD::ID&) = 0;
84
85         void invert () {
86                 _changes.removed.swap (_changes.added);
87         }
88
89         void get_changes_as_xml (XMLNode* history_node) const {
90
91                 XMLNode* child = new XMLNode (PBD::capitalize (property_name()));
92                 history_node->add_child_nocopy (*child);
93                 
94                 /* record the change described in our change member */
95
96                 if (!_changes.added.empty()) {
97                         for (typename ChangeContainer::iterator i = _changes.added.begin(); i != _changes.added.end(); ++i) {
98                                 XMLNode* add_node = new XMLNode ("Add");
99                                 child->add_child_nocopy (*add_node);
100                                 add_node->add_property ("id", (*i)->id().to_s());
101                         }
102                 }
103                 if (!_changes.removed.empty()) {
104                         for (typename ChangeContainer::iterator i = _changes.removed.begin(); i != _changes.removed.end(); ++i) {
105                                 XMLNode* remove_node = new XMLNode ("Remove");
106                                 child->add_child_nocopy (*remove_node);
107                                 remove_node->add_property ("id", (*i)->id().to_s());
108                         }
109                 }
110         }
111
112         bool set_value (XMLNode const &) {
113                 /* XXX: not used, but probably should be */
114                 assert (false);
115                 return false;
116         }
117
118         void get_value (XMLNode & node) const {
119                 for (typename Container::const_iterator i = _val.begin(); i != _val.end(); ++i) {
120                         node.add_child_nocopy ((*i)->get_state ());
121                 } 
122         }
123
124         bool changed () const {
125                 return !_changes.added.empty() || !_changes.removed.empty();
126         }
127         
128         void clear_history () {
129                 _changes.added.clear ();
130                 _changes.removed.clear ();
131         }
132
133         void apply_changes (PropertyBase const * p) {
134                 const ChangeRecord& change (dynamic_cast<const SequenceProperty*> (p)->changes ());
135                 update (change);
136         }
137
138         /** Given a record of changes to this property, pass it to a callback that will
139          *  update the property in some appropriate way. 
140          *
141          *  This exists because simply using std::sequence methods to add/remove items
142          *  from the property is far too simplistic - the semantics of add/remove may
143          *  be much more complex than that.
144          */
145         void update (const ChangeRecord& cr) {
146                 _update_callback (cr);
147         }
148
149         void get_changes_as_properties (PBD::PropertyList& changes, Command* cmd) const {
150                 if (changed ()) {
151                         SequenceProperty<Container>* a = copy_for_history ();
152                         changes.add (a);
153
154                         if (cmd) {
155                                 /* whenever one of the items emits DropReferences, make sure
156                                    that the Destructible we've been told to notify hears about
157                                    it. the Destructible is likely to be the Command being built
158                                    with this diff().
159                                 */
160                         
161                                 for (typename ChangeContainer::iterator i = a->changes().added.begin(); i != a->changes().added.end(); ++i) {
162                                         (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd));
163                                 }
164                         }
165                 }
166         }
167
168         SequenceProperty<Container>* maybe_clone_self_if_found_in_history_node (XMLNode const & node) const {
169
170                 XMLNodeList const children = node.children ();
171                 
172                 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
173
174                         if ((*i)->name() == capitalize (property_name())) {
175                                 
176                                 SequenceProperty<Container>* p = create ();
177                                 
178                                 if (p->set_changes (**i)) {
179                                         return p;
180                                 } else {
181                                         delete p;
182                                 }
183                         }
184                 }
185
186                 return 0;
187         }
188
189         void clear_owned_history () {
190                 for (typename Container::iterator i = begin(); i != end(); ++i) {
191                         (*i)->clear_history ();
192                 }
193         }
194
195         void rdiff (std::vector<StatefulDiffCommand*>& cmds) const {
196                 for (typename Container::const_iterator i = begin(); i != end(); ++i) {
197                         if ((*i)->changed ()) {
198                                 StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
199                                 cmds.push_back (sdc);
200                         }
201                 }
202         }
203
204         Container rlist() { return _val; }
205
206         /* Wrap salient methods of Sequence
207          */
208
209         typename Container::iterator begin() { return _val.begin(); }
210         typename Container::iterator end() { return _val.end(); }
211         typename Container::const_iterator begin() const { return _val.begin(); }
212         typename Container::const_iterator end() const { return _val.end(); }
213
214         typename Container::reverse_iterator rbegin() { return _val.rbegin(); }
215         typename Container::reverse_iterator rend() { return _val.rend(); }
216         typename Container::const_reverse_iterator rbegin() const { return _val.rbegin(); }
217         typename Container::const_reverse_iterator rend() const { return _val.rend(); }
218
219         typename Container::iterator insert (typename Container::iterator i, const typename Container::value_type& v) {
220                 _changes.add (v);
221                 return _val.insert (i, v);
222         }
223
224         typename Container::iterator erase (typename Container::iterator i) {
225                 if (i != _val.end()) {
226                         _changes.remove (*i);
227                 }
228                 return _val.erase (i);
229         }
230
231         typename Container::iterator erase (typename Container::iterator f, typename Container::iterator l) {
232                 for (typename Container::const_iterator i = f; i != l; ++i) {
233                         _changes.remove (*i);
234                 }
235                 return _val.erase (f, l);
236         }
237
238         void push_back (const typename Container::value_type& v) {
239                 _changes.add (v);
240                 _val.push_back (v);
241         }
242
243         void push_front (const typename Container::value_type& v) {
244                 _changes.add (v);
245                 _val.push_front (v);
246         }
247
248         void pop_front () {
249                 if (!_val.empty()) {
250                         _changes.remove (front());
251                 }
252                 _val.pop_front ();
253         }
254
255         void pop_back () {
256                 if (!_val.empty()) {
257                         _changes.remove (back());
258                 }
259                 _val.pop_back ();
260         }
261
262         void clear () {
263                 for (typename Container::iterator i = _val.begin(); i != _val.end(); ++i) {
264                         _changes.remove (*i);
265                 }
266                 _val.clear ();
267         }
268         
269         typename Container::size_type size() const { 
270                 return _val.size();
271         }
272
273         bool empty() const { 
274                 return _val.empty();
275         }
276
277         Container& operator= (const Container& other) {
278                 for (typename Container::iterator i = _val.begin(); i != _val.end(); ++i) {
279                         _changes.remove (*i);
280                 }
281                 for (typename Container::iterator i = other.begin(); i != other.end(); ++i) {
282                         _changes.add (*i);
283                 }
284                 return _val = other;
285         }
286
287         typename Container::reference front() { 
288                 return _val.front ();
289         }
290
291         typename Container::const_reference front() const { 
292                 return _val.front ();
293         }
294
295         typename Container::reference back() { 
296                 return _val.back ();
297         }
298
299         typename Container::const_reference back() const { 
300                 return _val.back ();
301         }
302
303         void sort() { 
304                 _val.sort ();
305         }
306
307         template<class BinaryPredicate> void sort(BinaryPredicate comp) {
308                 _val.sort (comp);
309         }
310         
311         const ChangeRecord& changes () const { return _changes; }
312
313   protected:
314         Container _val;
315         ChangeRecord _changes;
316         boost::function<void(const ChangeRecord&)> _update_callback;
317
318         /** Load serialized change history.
319          * @return true if loading succeeded, false otherwise
320          */
321
322         bool set_changes (XMLNode const & history_node) {
323
324                 const XMLNodeList& children (history_node.children());
325
326                 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
327                         const XMLProperty* prop = (*i)->property ("id");
328                         if (prop) {
329                                 PBD::ID id (prop->value());
330                                 typename Container::value_type v = lookup_id (id);
331                                 if (!v) {
332                                         std::cerr << "No such item, ID = " << id.to_s() << " (from " << prop->value() << ")\n";
333                                         return false;
334                                 }
335                                 if ((*i)->name() == "Add") {
336                                         _changes.added.insert (v);
337                                 } else if ((*i)->name() == "Remove") {
338                                         _changes.removed.insert (v);
339                                 }
340                         }
341                 }
342
343                 return true;
344         }
345
346 private:
347         virtual SequenceProperty<Container>* create () const = 0;
348
349         /* create a copy of this ListSequenceProperty that only
350            has what is needed for use in a history list command. This
351            means that it won't contain the actual item list but
352            will have the added/removed list.
353         */
354         
355         SequenceProperty<Container>* copy_for_history () const {
356                 SequenceProperty<Container>* copy = create ();
357                 /* this is all we need */
358                 copy->_changes = _changes;
359                 return copy;
360         }
361 };
362
363 }
364
365 #endif /* __libpbd_sequence_property_h__ */