clear_history -> clear_changes and some comments.
[ardour.git] / libs / pbd / pbd / properties.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 __pbd_properties_h__
21 #define __pbd_properties_h__
22
23 #include <string>
24 #include <sstream>
25 #include <list>
26 #include <set>
27 #include <iostream>
28
29 #include "pbd/xml++.h"
30 #include "pbd/property_basics.h"
31 #include "pbd/property_list.h"
32 #include "pbd/enumwriter.h"
33
34 namespace PBD {
35
36 /** Parent class for classes which represent a single scalar property in a Stateful object 
37  */
38 template<class T>
39 class PropertyTemplate : public PropertyBase
40 {
41 public:
42         PropertyTemplate (PropertyDescriptor<T> p, T const& v)
43                 : PropertyBase (p.property_id)
44                 , _have_old (false)
45                 , _current (v)
46         {}
47
48         PropertyTemplate (PropertyDescriptor<T> p, T const& o, T const& c)
49                 : PropertyBase (p.property_id)
50                 , _have_old (true)
51                 , _current (c)
52                 , _old (o)
53         {}
54         
55         PropertyTemplate<T>& operator=(PropertyTemplate<T> const& s) {
56                 /* XXX: isn't there a nicer place to do this? */
57                 _have_old    = s._have_old;
58                 _property_id = s._property_id;
59
60                 _current = s._current;
61                 _old     = s._old;
62                 return *this;
63         }
64
65         T & operator=(T const& v) {
66                 set (v);
67                 return _current;
68         }
69
70         T & operator+=(T const& v) {
71                 set (_current + v);
72                 return _current;
73         }
74
75         bool operator==(const T& other) const {
76                 return _current == other;
77         }
78
79         bool operator!=(const T& other) const {
80                 return _current != other;
81         }
82
83         operator T const &() const {
84                 return _current;
85         }
86
87         T const& val () const {
88                 return _current;
89         }
90
91         void clear_changes () {
92                 _have_old = false;
93         }
94
95         void get_changes_as_xml (XMLNode* history_node) const {
96                 XMLNode* node = history_node->add_child (property_name());
97                 node->add_property ("from", to_string (_old));
98                 node->add_property ("to", to_string (_current));
99         }
100
101         bool set_value (XMLNode const & node) {
102
103                 XMLProperty const* p = node.property (property_name());
104
105                 if (p) {
106                         T const v = from_string (p->value ());
107
108                         if (v != _current) {
109                                 set (v);
110                                 return true;
111                         }
112                 }
113
114                 return false;
115         }
116
117         void get_value (XMLNode & node) const {
118                 node.add_property (property_name(), to_string (_current));
119         }
120
121         bool changed () const { return _have_old; }
122         
123         void apply_changes (PropertyBase const * p) {
124                 T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val ();
125                 std::cout << "Apply changes: " << v << " cf " << _current << "\n";
126                 if (v != _current) {
127                         set (v);
128                 }
129         }
130
131         void invert () {
132                 T const tmp = _current;
133                 _current = _old;
134                 _old = tmp;
135                 std::cout << "Inverted to " << _old << " -> " << _current << "\n";
136         }
137
138 protected:
139         /** Constructs a PropertyTemplate with a default
140             value for _old and _current.
141         */
142
143         PropertyTemplate (PropertyDescriptor<T> p)
144                 : PropertyBase (p.property_id)
145         {}
146
147         void set (T const& v) {
148                 if (v != _current) {
149                         if (!_have_old) {
150                                 _old = _current;
151                                 _have_old = true;
152                         } else {
153                                 if (v == _old) {
154                                         /* value has been reset to the value
155                                            at the start of a history transaction,
156                                            before clear_changes() is called.
157                                            thus there is effectively no apparent
158                                            history for this property.
159                                         */
160                                         _have_old = false;
161                                 }
162                         }
163
164                         _current  = v;
165                 } 
166         }
167
168         virtual std::string to_string (T const& v) const             = 0;
169         virtual T           from_string (std::string const& s) const = 0;
170
171         bool _have_old;
172         T _current;
173         T _old;
174 };
175
176 template<class T>
177 std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
178 {
179         return os << s.val ();
180 }
181
182 /** Representation of a single piece of scalar state in a Stateful; for use
183  *  with types that can be written to / read from stringstreams.
184  */
185 template<class T>
186 class Property : public PropertyTemplate<T>
187 {
188 public:
189         Property (PropertyDescriptor<T> q, T const& v)
190                 : PropertyTemplate<T> (q, v)
191         {}
192
193         Property (PropertyDescriptor<T> q, T const& o, T const& c)
194                 : PropertyTemplate<T> (q, o, c)
195         {}
196
197         Property<T>* clone () const {
198                 return new Property<T> (*this);
199         }
200         
201         void get_changes_as_properties (PropertyList& changes, Command *) const {
202                 if (this->_have_old) {
203                         changes.add (new Property<T> (*this));
204                 }
205         }
206
207         Property<T>* clone_from_xml (const XMLNode& node) const {
208                 XMLNodeList const & children = node.children ();
209                 XMLNodeList::const_iterator i = children.begin();
210                 while (i != children.end() && (*i)->name() != this->property_name()) {
211                         ++i;
212                 }
213
214                 if (i == children.end()) {
215                         return 0;
216                 }
217                 XMLProperty* from = (*i)->property ("from");
218                 XMLProperty* to = (*i)->property ("to");
219                                 
220                 if (!from || !to) {
221                         return 0;
222                 }
223                         
224                 return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ()));
225         }
226
227         T & operator=(T const& v) {
228                 this->set (v);
229                 return this->_current;
230         }
231
232 private:
233         friend class PropertyFactory;
234
235         Property (PropertyDescriptor<T> q)
236                 : PropertyTemplate<T> (q)
237         {}
238
239         /* Note that we do not set a locale for the streams used
240          * in to_string() or from_string(), because we want the
241          * format to be portable across locales (i.e. C or
242          * POSIX). Also, there is the small matter of
243          * std::locale aborting on OS X if used with anything
244          * other than C or POSIX locales.
245          */
246         virtual std::string to_string (T const& v) const {
247                 std::stringstream s;
248                 s.precision (12); // in case its floating point
249                 s << v;
250                 return s.str ();
251         }
252
253         virtual T from_string (std::string const& s) const {
254                 std::stringstream t (s);
255                 T                 v;
256                 t >> v;
257                 return v;
258         }
259
260 };
261
262 /** Specialization, for std::string which is common and special (see to_string() and from_string()
263  *  Using stringstream to read from a std::string is easy to get wrong because of whitespace
264  *  separators, etc.
265  */
266 template<>
267 class Property<std::string> : public PropertyTemplate<std::string>
268 {
269 public:
270         Property (PropertyDescriptor<std::string> q, std::string const& v)
271                 : PropertyTemplate<std::string> (q, v)
272         {}
273
274         Property<std::string>* clone () const {
275                 return new Property<std::string> (*this);
276         }
277         
278         void get_changes_as_properties (PropertyList& changes, Command* /*ignored*/) const {
279                 if (this->_have_old) {
280                         changes.add (new Property<std::string> (*this));
281                 }
282         }
283
284         std::string & operator=(std::string const& v) {
285                 this->set (v);
286                 return this->_current;
287         }
288
289 private:
290         std::string to_string (std::string const& v) const {
291                 return _current;
292         }
293
294         std::string from_string (std::string const& s) const {
295                 return s;
296         }
297
298 };
299
300 template<class T>
301 class EnumProperty : public Property<T>
302 {
303 public:
304         EnumProperty (PropertyDescriptor<T> q, T const& v)
305                 : Property<T> (q, v)
306         {}
307
308         T & operator=(T const& v) {
309                 this->set (v);
310                 return this->_current;
311         }
312
313 private:
314         std::string to_string (T const & v) const {
315                 return enum_2_string (v);
316         }
317
318         T from_string (std::string const & s) const {
319                 return static_cast<T> (string_2_enum (s, this->_current));
320         }
321 };
322         
323 } /* namespace PBD */
324
325 #include "pbd/property_list_impl.h"
326 #include "pbd/property_basics_impl.h"
327
328 #endif /* __pbd_properties_h__ */