Ardour  9.0-pre0-582-g084a23a80d
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 #pragma once
22 
23 #include <iostream>
24 
25 #include <set>
26 #include <list>
27 
28 
29 #include "pbd/libpbd_visibility.h"
30 #include "pbd/convert.h"
31 #include "pbd/id.h"
32 #include "pbd/property_basics.h"
33 #include "pbd/property_list.h"
35 #include "pbd/error.h"
36 
37 namespace PBD {
38 class Command;
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 
81  };
82 
83  SequenceProperty (PropertyID id, const std::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_change (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, std::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<PBD::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;
361  std::function<void(const ChangeRecord&)> _update_callback;
362 
363 private:
364  virtual SequenceProperty<Container>* create () const = 0;
365 };
366 
367 }
368 
virtual void drop_references()
Definition: destructible.h:33
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()
SequenceProperty(PropertyID id, const std::function< void(const ChangeRecord &)> &update)
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)
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)
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)
std::function< void(const ChangeRecord &)> _update_callback
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 &)
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:71
std::vector< XMLNode * > XMLNodeList
Definition: xml++.h:66