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