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