resolve merge with master (?)
[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 #include "pbd/stateful_diff_command.h"
35 #include "pbd/error.h"
36
37 namespace PBD {
38
39 /** A base class for properties whose state is a container of other
40  *  things.  Its behaviour is `specialised' for this purpose in that
41  *  it holds state changes as additions to and removals from the
42  *  container, which is more efficient than storing entire state after
43  *  any change.
44  */
45 template<typename Container>
46 class SequenceProperty : public PropertyBase
47 {
48   public:
49         typedef std::set<typename Container::value_type> ChangeContainer;
50
51         /** A record of changes made */
52         struct ChangeRecord {
53
54                 void add (typename Container::value_type const & r) {
55                         typename ChangeContainer::iterator i = removed.find (r);
56                         if (i != removed.end()) {
57                                 /* we're adding, and this thing has already been marked as removed, so
58                                    just remove it from the removed list
59                                 */
60                                 removed.erase (r);
61                         } else {
62                                 added.insert (r);
63                         }
64                 }
65
66                 void remove (typename Container::value_type const & r) {
67                         typename ChangeContainer::iterator i = added.find (r);
68                         if (i != added.end()) {
69                                 /* we're removing, and this thing has already been marked as added, so
70                                    just remove it from the added list
71                                 */
72                                 added.erase (i);
73                         } else {
74                                 removed.insert (r);
75                         }
76                 }
77
78                 ChangeContainer added;
79                 ChangeContainer removed;
80         };
81
82         SequenceProperty (PropertyID id, const boost::function<void(const ChangeRecord&)>& update)
83                 : PropertyBase (id), _update_callback (update) {}
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::const_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                                 get_content_as_xml (*i, *add_node);
101                         }
102                 }
103                 if (!_changes.removed.empty()) {
104                         for (typename ChangeContainer::const_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                                 get_content_as_xml (*i, *remove_node);
108                         }
109                 }
110         }
111
112         /** Get a representation of one of our items as XML.  The representation must be sufficient to
113          *  restore the item's state later; an ID is ok if someone else is storing the item state,
114          *  otherwise it needs to be the full state.  The supplied node is an \<Add\> or \<Remove\>
115          *  which this method can either add properties or children to.
116          */
117         virtual void get_content_as_xml (typename ChangeContainer::value_type, XMLNode &) const = 0;
118
119         bool set_value (XMLNode const &) {
120                 /* XXX: not used, but probably should be */
121                 assert (false);
122                 return false;
123         }
124
125         void get_value (XMLNode & node) const {
126                 for (typename Container::const_iterator i = _val.begin(); i != _val.end(); ++i) {
127                         node.add_child_nocopy ((*i)->get_state ());
128                 } 
129         }
130
131         bool changed () const {
132                 return !_changes.added.empty() || !_changes.removed.empty();
133         }
134         
135         void clear_changes () {
136                 _changes.added.clear ();
137                 _changes.removed.clear ();
138         }
139
140         void apply_changes (PropertyBase const * p) {
141                 const ChangeRecord& change (dynamic_cast<const SequenceProperty*> (p)->changes ());
142                 update (change);
143         }
144
145         /** Given a record of changes to this property, pass it to a callback that will
146          *  update the property in some appropriate way. 
147          *
148          *  This exists because simply using std::sequence methods to add/remove items
149          *  from the property is far too simplistic - the semantics of add/remove may
150          *  be much more complex than that.
151          */
152         void update (const ChangeRecord& cr) {
153                 _update_callback (cr);
154         }
155
156         void get_changes_as_properties (PBD::PropertyList& changes, Command* cmd) const {
157                 if (!changed ()) {
158                         return;
159                 }
160                 
161                 /* Create a property with just the changes and not the actual values */
162                 SequenceProperty<Container>* a = create ();
163                 a->_changes = _changes;
164                 changes.add (a);
165                 
166                 if (cmd) {
167                         /* whenever one of the items emits DropReferences, make sure
168                            that the Destructible we've been told to notify hears about
169                            it. the Destructible is likely to be the Command being built
170                            with this diff().
171                         */
172                         
173                         for (typename ChangeContainer::const_iterator i = a->changes().added.begin(); i != a->changes().added.end(); ++i) {
174                                 (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd));
175                         }
176                 }
177         }
178
179         SequenceProperty<Container>* clone_from_xml (XMLNode const & node) const {
180
181                 XMLNodeList const children = node.children ();
182
183                 /* find the node for this property name */
184                 
185                 std::string const c = capitalize (property_name ());
186                 XMLNodeList::const_iterator i = children.begin();
187                 while (i != children.end() && (*i)->name() != c) {
188                         ++i;
189                 }
190
191                 if (i == children.end()) {
192                         return 0;
193                 }
194
195                 /* create a property with the changes */
196                 
197                 SequenceProperty<Container>* p = create ();
198
199                 XMLNodeList const & grandchildren = (*i)->children ();
200                 for (XMLNodeList::const_iterator j = grandchildren.begin(); j != grandchildren.end(); ++j) {
201
202                         typename Container::value_type v = get_content_from_xml (**j);
203
204                         if (!v) {
205                                 warning << "undo transaction references an unknown object" << endmsg;
206                         } else if ((*j)->name() == "Add") {
207                                 p->_changes.added.insert (v);
208                         } else if ((*j)->name() == "Remove") {
209                                 p->_changes.removed.insert (v);
210                         }
211                 }
212
213                 return p;
214         }
215
216         /** Given an \<Add\> or \<Remove\> node as passed into get_content_to_xml, obtain an item */
217         virtual typename Container::value_type get_content_from_xml (XMLNode const & node) const = 0;
218
219         void clear_owned_changes () {
220                 for (typename Container::iterator i = begin(); i != end(); ++i) {
221                         (*i)->clear_changes ();
222                 }
223         }
224
225         void rdiff (std::vector<Command*>& cmds) const {
226                 for (typename Container::const_iterator i = begin(); i != end(); ++i) {
227                         if ((*i)->changed ()) {
228                                 StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
229                                 cmds.push_back (sdc);
230                         }
231                 }
232         }
233
234         Container rlist() const { return _val; }
235
236         /* Wrap salient methods of Sequence
237          */
238
239         typename Container::iterator begin() { return _val.begin(); }
240         typename Container::iterator end() { return _val.end(); }
241         typename Container::const_iterator begin() const { return _val.begin(); }
242         typename Container::const_iterator end() const { return _val.end(); }
243
244         typename Container::reverse_iterator rbegin() { return _val.rbegin(); }
245         typename Container::reverse_iterator rend() { return _val.rend(); }
246         typename Container::const_reverse_iterator rbegin() const { return _val.rbegin(); }
247         typename Container::const_reverse_iterator rend() const { return _val.rend(); }
248
249         typename Container::iterator insert (typename Container::iterator i, const typename Container::value_type& v) {
250                 _changes.add (v);
251                 return _val.insert (i, v);
252         }
253
254         typename Container::iterator erase (typename Container::iterator i) {
255                 if (i != _val.end()) {
256                         _changes.remove (*i);
257                 }
258                 return _val.erase (i);
259         }
260
261         typename Container::iterator erase (typename Container::iterator f, typename Container::iterator l) {
262                 for (typename Container::const_iterator i = f; i != l; ++i) {
263                         _changes.remove (*i);
264                 }
265                 return _val.erase (f, l);
266         }
267
268         void remove (const typename Container::value_type& v) {
269                 _changes.remove (v);
270                 _val.remove (v);
271         }
272
273         void push_back (const typename Container::value_type& v) {
274                 _changes.add (v);
275                 _val.push_back (v);
276         }
277
278         void push_front (const typename Container::value_type& v) {
279                 _changes.add (v);
280                 _val.push_front (v);
281         }
282
283         void pop_front () {
284                 if (!_val.empty()) {
285                         _changes.remove (front());
286                 }
287                 _val.pop_front ();
288         }
289
290         void pop_back () {
291                 if (!_val.empty()) {
292                         _changes.remove (back());
293                 }
294                 _val.pop_back ();
295         }
296
297         void clear () {
298                 for (typename Container::iterator i = _val.begin(); i != _val.end(); ++i) {
299                         _changes.remove (*i);
300                 }
301                 _val.clear ();
302         }
303         
304         typename Container::size_type size() const { 
305                 return _val.size();
306         }
307
308         bool empty() const { 
309                 return _val.empty();
310         }
311
312         Container& operator= (const Container& other) {
313                 for (typename Container::iterator i = _val.begin(); i != _val.end(); ++i) {
314                         _changes.remove (*i);
315                 }
316                 for (typename Container::iterator i = other.begin(); i != other.end(); ++i) {
317                         _changes.add (*i);
318                 }
319                 return _val = other;
320         }
321
322         typename Container::reference front() { 
323                 return _val.front ();
324         }
325
326         typename Container::const_reference front() const { 
327                 return _val.front ();
328         }
329
330         typename Container::reference back() { 
331                 return _val.back ();
332         }
333
334         typename Container::const_reference back() const { 
335                 return _val.back ();
336         }
337
338         void sort() { 
339                 _val.sort ();
340         }
341
342         template<class BinaryPredicate> void sort(BinaryPredicate comp) {
343                 _val.sort (comp);
344         }
345         
346         const ChangeRecord& changes () const { return _changes; }
347
348 protected:
349
350         /* copy construction only by subclasses */
351         SequenceProperty (SequenceProperty<Container> const & p)
352                 : PropertyBase (p)
353                 , _val (p._val)
354                 , _changes (p._changes)
355                 , _update_callback (p._update_callback)
356         {}
357         
358         Container _val; ///< our actual container of things
359         ChangeRecord _changes; ///< changes to the container (adds/removes) that have happened since clear_changes() was last called
360         boost::function<void(const ChangeRecord&)> _update_callback;
361
362 private:        
363         virtual SequenceProperty<Container>* create () const = 0;
364 };
365
366 }
367
368 #endif /* __libpbd_sequence_property_h__ */