ardour
sequence_property.h
Go to the documentation of this file.
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"
36 #include "pbd/error.h"
37 
38 namespace PBD {
39 
46 template<typename Container>
47 class /*LIBPBD_API*/ SequenceProperty : public PropertyBase
48 {
49  public:
50  typedef std::set<typename Container::value_type> ChangeContainer;
51 
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)
85 
86  void invert () {
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 
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 
153  void update (const ChangeRecord& cr) {
154  _update_callback (cr);
155  }
156 
158  if (!changed ()) {
159  return;
160  }
161 
162  /* Create a property with just the changes and not the actual values */
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 
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 
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 
218  virtual typename Container::value_type get_content_from_xml (XMLNode const & node) const = 0;
219 
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 ()) {
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 */
353  : PropertyBase (p)
354  , _val (p._val)
355  , _changes (p._changes)
357  {}
358 
359  Container _val;
360  ChangeRecord _changes;
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__ */
void push_back(const typename Container::value_type &v)
void remove(typename Container::value_type const &r)
string capitalize(const string &str)
Definition: convert.cc:47
Container::const_iterator end() const
virtual Container::value_type get_content_from_xml(XMLNode const &node) const =0
void add(typename Container::value_type const &r)
Container::reverse_iterator rend()
Container::iterator begin()
void push_front(const typename Container::value_type &v)
void update(const ChangeRecord &cr)
tuple f
Definition: signals.py:35
LIBPBD_API Transmitter warning
const XMLNodeList & children(const std::string &str=std::string()) const
Definition: xml++.cc:329
std::set< typename Container::value_type > ChangeContainer
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
void sort(BinaryPredicate comp)
Container::iterator insert(typename Container::iterator i, const typename Container::value_type &v)
Container::const_reverse_iterator rbegin() const
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
GQuark PropertyID
class LIBPBD_API StatefulDiffCommand
void remove_node(GraphNode *node)
bool set_value(XMLNode const &)
Container rlist() const
Container::iterator end()
Container::iterator erase(typename Container::iterator i)
ChangeRecord _changes
changes to the container (adds/removes) that have happened since clear_changes() was last called ...
void apply_changes(PropertyBase const *p)
void get_value(XMLNode &node) const
SequenceProperty(SequenceProperty< Container > const &p)
void drop_references()
Definition: destructible.h:36
Container::reverse_iterator rbegin()
Container::const_iterator begin() const
void get_changes_as_properties(PBD::PropertyList &changes, Command *cmd) const
Container::size_type size() const
const ChangeRecord & changes() const
void add_child_nocopy(XMLNode &)
Definition: xml++.cc:357
void get_changes_as_xml(XMLNode *history_node) const
const gchar * property_name() const
Container _val
our actual container of things
Definition: xml++.h:95
Container::reference back()
SequenceProperty(PropertyID id, const boost::function< void(const ChangeRecord &)> &update)
Definition: debug.h:30
virtual SequenceProperty< Container > * create() const =0
void rdiff(std::vector< Command * > &cmds) const
Container::iterator erase(typename Container::iterator f, typename Container::iterator l)
Container::const_reference back() const
boost::function< void(const ChangeRecord &)> _update_callback
Container::const_reverse_iterator rend() const
Container::const_reference front() const
SequenceProperty< Container > * clone_from_xml(XMLNode const &node) const
bool add(PropertyBase *prop)
virtual void get_content_as_xml(typename ChangeContainer::value_type, XMLNode &) const =0
Container & operator=(const Container &other)
Container::reference front()