Ardour  8.12
sequence_property.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2010-2015 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #ifndef __libpbd_sequence_property_h__
22 #define __libpbd_sequence_property_h__
23 
24 #include <iostream>
25 
26 #include <set>
27 #include <list>
28 
29 #include <boost/function.hpp>
30 
31 #include "pbd/libpbd_visibility.h"
32 #include "pbd/convert.h"
33 #include "pbd/id.h"
34 #include "pbd/property_basics.h"
35 #include "pbd/property_list.h"
37 #include "pbd/error.h"
38 
39 namespace PBD {
40 class Command;
41 
48 template<typename Container>
49 class /*LIBPBD_API*/ SequenceProperty : public PropertyBase
50 {
51  public:
52  typedef std::set<typename Container::value_type> ChangeContainer;
53 
55  struct ChangeRecord {
56 
57  void add (typename Container::value_type const & r) {
58  typename ChangeContainer::iterator i = removed.find (r);
59  if (i != removed.end()) {
60  /* we're adding, and this thing has already been marked as removed, so
61  just remove it from the removed list
62  */
63  removed.erase (r);
64  } else {
65  added.insert (r);
66  }
67  }
68 
69  void remove (typename Container::value_type const & r) {
70  typename ChangeContainer::iterator i = added.find (r);
71  if (i != added.end()) {
72  /* we're removing, and this thing has already been marked as added, so
73  just remove it from the added list
74  */
75  added.erase (i);
76  } else {
77  removed.insert (r);
78  }
79  }
80 
83  };
84 
85  SequenceProperty (PropertyID id, const boost::function<void(const ChangeRecord&)>& update)
87 
88  void invert () {
90  }
91 
92  void get_changes_as_xml (XMLNode* history_node) const {
93 
94  XMLNode* child = new XMLNode (PBD::capitalize (property_name()));
95  history_node->add_child_nocopy (*child);
96 
97  /* record the change described in our change member */
98 
99  if (!_changes.added.empty()) {
100  for (typename ChangeContainer::const_iterator i = _changes.added.begin(); i != _changes.added.end(); ++i) {
101  XMLNode* add_node = new XMLNode ("Add");
102  child->add_child_nocopy (*add_node);
103  get_content_as_xml (*i, *add_node);
104  }
105  }
106  if (!_changes.removed.empty()) {
107  for (typename ChangeContainer::const_iterator i = _changes.removed.begin(); i != _changes.removed.end(); ++i) {
108  XMLNode* remove_node = new XMLNode ("Remove");
109  child->add_child_nocopy (*remove_node);
111  }
112  }
113  }
114 
120  virtual void get_content_as_xml (typename ChangeContainer::value_type, XMLNode &) const = 0;
121 
122  bool set_value (XMLNode const &) {
123  /* XXX: not used, but probably should be */
124  assert (false);
125  return false;
126  }
127 
128  void get_value (XMLNode & node) const {
129  for (typename Container::const_iterator i = _val.begin(); i != _val.end(); ++i) {
130  node.add_child_nocopy ((*i)->get_state ());
131  }
132  }
133 
134  bool changed () const {
135  return !_changes.added.empty() || !_changes.removed.empty();
136  }
137 
138  void clear_changes () {
139  _changes.added.clear ();
140  _changes.removed.clear ();
141  }
142 
143  void apply_change (PropertyBase const * p) {
144  const ChangeRecord& change (dynamic_cast<const SequenceProperty*> (p)->changes ());
145  update (change);
146  }
147 
155  void update (const ChangeRecord& cr) {
156  _update_callback (cr);
157  }
158 
160  if (!changed ()) {
161  return;
162  }
163 
164  /* Create a property with just the changes and not the actual values */
166  a->_changes = _changes;
167  changes.add (a);
168 
169  if (cmd) {
170  /* whenever one of the items emits DropReferences, make sure
171  that the Destructible we've been told to notify hears about
172  it. the Destructible is likely to be the Command being built
173  with this diff().
174  */
175 
176  for (typename ChangeContainer::const_iterator i = a->changes().added.begin(); i != a->changes().added.end(); ++i) {
177  (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd));
178  }
179  }
180  }
181 
183 
184  XMLNodeList const children = node.children ();
185 
186  /* find the node for this property name */
187 
188  std::string const c = capitalize (property_name ());
189  XMLNodeList::const_iterator i = children.begin();
190  while (i != children.end() && (*i)->name() != c) {
191  ++i;
192  }
193 
194  if (i == children.end()) {
195  return 0;
196  }
197 
198  /* create a property with the changes */
199 
201 
202  XMLNodeList const & grandchildren = (*i)->children ();
203  for (XMLNodeList::const_iterator j = grandchildren.begin(); j != grandchildren.end(); ++j) {
204 
205  typename Container::value_type v = get_content_from_xml (**j);
206 
207  if (!v) {
208  warning << "undo transaction references an unknown object" << endmsg;
209  } else if ((*j)->name() == "Add") {
210  p->_changes.added.insert (v);
211  } else if ((*j)->name() == "Remove") {
212  p->_changes.removed.insert (v);
213  }
214  }
215 
216  return p;
217  }
218 
220  virtual typename Container::value_type get_content_from_xml (XMLNode const & node) const = 0;
221 
223  for (typename Container::iterator i = begin(); i != end(); ++i) {
224  (*i)->clear_changes ();
225  }
226  }
227 
228  void rdiff (std::vector<PBD::Command*>& cmds) const {
229  for (typename Container::const_iterator i = begin(); i != end(); ++i) {
230  if ((*i)->changed ()) {
232  cmds.push_back (sdc);
233  }
234  }
235  }
236 
237  Container rlist() const { return _val; }
238 
239  /* Wrap salient methods of Sequence
240  */
241 
242  typename Container::iterator begin() { return _val.begin(); }
243  typename Container::iterator end() { return _val.end(); }
244  typename Container::const_iterator begin() const { return _val.begin(); }
245  typename Container::const_iterator end() const { return _val.end(); }
246 
247  typename Container::reverse_iterator rbegin() { return _val.rbegin(); }
248  typename Container::reverse_iterator rend() { return _val.rend(); }
249  typename Container::const_reverse_iterator rbegin() const { return _val.rbegin(); }
250  typename Container::const_reverse_iterator rend() const { return _val.rend(); }
251 
252  typename Container::iterator insert (typename Container::iterator i, const typename Container::value_type& v) {
253  _changes.add (v);
254  return _val.insert (i, v);
255  }
256 
257  typename Container::iterator erase (typename Container::iterator i) {
258  if (i != _val.end()) {
259  _changes.remove (*i);
260  }
261  return _val.erase (i);
262  }
263 
264  typename Container::iterator erase (typename Container::iterator f, typename Container::iterator l) {
265  for (typename Container::const_iterator i = f; i != l; ++i) {
266  _changes.remove (*i);
267  }
268  return _val.erase (f, l);
269  }
270 
271  void remove (const typename Container::value_type& v) {
272  _changes.remove (v);
273  _val.remove (v);
274  }
275 
276  void push_back (const typename Container::value_type& v) {
277  _changes.add (v);
278  _val.push_back (v);
279  }
280 
281  void push_front (const typename Container::value_type& v) {
282  _changes.add (v);
283  _val.push_front (v);
284  }
285 
286  void pop_front () {
287  if (!_val.empty()) {
288  _changes.remove (front());
289  }
290  _val.pop_front ();
291  }
292 
293  void pop_back () {
294  if (!_val.empty()) {
295  _changes.remove (back());
296  }
297  _val.pop_back ();
298  }
299 
300  void clear () {
301  for (typename Container::iterator i = _val.begin(); i != _val.end(); ++i) {
302  _changes.remove (*i);
303  }
304  _val.clear ();
305  }
306 
307  typename Container::size_type size() const {
308  return _val.size();
309  }
310 
311  bool empty() const {
312  return _val.empty();
313  }
314 
315  Container& operator= (const Container& other) {
316  for (typename Container::const_iterator i = _val.begin(); i != _val.end(); ++i) {
317  _changes.remove (*i);
318  }
319  for (typename Container::const_iterator i = other.begin(); i != other.end(); ++i) {
320  _changes.add (*i);
321  }
322  return _val = other;
323  }
324 
325  typename Container::reference front() {
326  return _val.front ();
327  }
328 
329  typename Container::const_reference front() const {
330  return _val.front ();
331  }
332 
333  typename Container::reference back() {
334  return _val.back ();
335  }
336 
337  typename Container::const_reference back() const {
338  return _val.back ();
339  }
340 
341  void sort() {
342  _val.sort ();
343  }
344 
345  template<class BinaryPredicate> void sort(BinaryPredicate comp) {
346  _val.sort (comp);
347  }
348 
349  const ChangeRecord& changes () const { return _changes; }
350 
351 protected:
352 
353  /* copy construction only by subclasses */
355  : PropertyBase (p)
356  , _val (p._val)
357  , _changes (p._changes)
359  {}
360 
361  Container _val;
363  boost::function<void(const ChangeRecord&)> _update_callback;
364 
365 private:
366  virtual SequenceProperty<Container>* create () const = 0;
367 };
368 
369 }
370 
371 #endif /* __libpbd_sequence_property_h__ */
virtual void drop_references()
Definition: destructible.h:34
const gchar * property_name() const
void get_changes_as_xml(XMLNode *history_node) const
void apply_change(PropertyBase const *p)
Container::const_iterator end() const
Container::size_type size() const
Container::reverse_iterator rbegin()
Container::iterator erase(typename Container::iterator i)
void get_value(XMLNode &node) const
Container::const_reference back() const
Container::reference back()
Container::iterator end()
void remove(const typename Container::value_type &v)
Container::reference front()
std::set< typename Container::value_type > ChangeContainer
void push_back(const typename Container::value_type &v)
Container rlist() const
Container::iterator insert(typename Container::iterator i, const typename Container::value_type &v)
Container & operator=(const Container &other)
virtual Container::value_type get_content_from_xml(XMLNode const &node) const =0
Container::const_iterator begin() const
SequenceProperty< Container > * clone_from_xml(XMLNode const &node) const
Container::const_reverse_iterator rend() const
void push_front(const typename Container::value_type &v)
boost::function< void(const ChangeRecord &)> _update_callback
Container::const_reference front() const
void sort(BinaryPredicate comp)
Container::reverse_iterator rend()
void rdiff(std::vector< PBD::Command * > &cmds) const
void update(const ChangeRecord &cr)
SequenceProperty(PropertyID id, const boost::function< void(const ChangeRecord &)> &update)
void get_changes_as_properties(PBD::PropertyList &changes, Command *cmd) const
virtual void get_content_as_xml(typename ChangeContainer::value_type, XMLNode &) const =0
virtual SequenceProperty< Container > * create() const =0
Container _val
our actual container of things
Container::const_reverse_iterator rbegin() const
Container::iterator erase(typename Container::iterator f, typename Container::iterator l)
SequenceProperty(SequenceProperty< Container > const &p)
bool set_value(XMLNode const &)
Container::iterator begin()
ChangeRecord _changes
changes to the container (adds/removes) that have happened since clear_changes() was last called
const ChangeRecord & changes() const
Definition: xml++.h:114
const XMLNodeList & children(const std::string &str=std::string()) const
void add_child_nocopy(XMLNode &)
void remove_node(GraphNode *node)
Definition: axis_view.h:42
std::string capitalize(const std::string &)
GQuark PropertyID
Transmitter warning
void remove(typename Container::value_type const &r)
void add(typename Container::value_type const &r)
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:72
std::vector< XMLNode * > XMLNodeList
Definition: xml++.h:66