bd4846dc7e03b715849b53db121821f62c620894
[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                 if (v != _current) {
126                         set (v);
127                 }
128         }
129
130         void invert () {
131                 T const tmp = _current;
132                 _current = _old;
133                 _old = tmp;
134         }
135
136         void get_changes_as_properties (PropertyList& changes, Command *) const {
137                 if (this->_have_old) {
138                         changes.add (clone ());
139                 }
140         }
141
142 protected:
143         /** Constructs a PropertyTemplate with a default
144             value for _old and _current.
145         */
146
147         PropertyTemplate (PropertyDescriptor<T> p)
148                 : PropertyBase (p.property_id)
149         {}
150
151         void set (T const& v) {
152                 if (v != _current) {
153                         if (!_have_old) {
154                                 _old = _current;
155                                 _have_old = true;
156                         } else {
157                                 if (v == _old) {
158                                         /* value has been reset to the value
159                                            at the start of a history transaction,
160                                            before clear_changes() is called.
161                                            thus there is effectively no apparent
162                                            history for this property.
163                                         */
164                                         _have_old = false;
165                                 }
166                         }
167
168                         _current  = v;
169                 } 
170         }
171
172         virtual std::string to_string (T const& v) const             = 0;
173         virtual T           from_string (std::string const& s) const = 0;
174
175         bool _have_old;
176         T _current;
177         T _old;
178 };
179
180 template<class T>
181 std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
182 {
183         return os << s.val ();
184 }
185
186 /** Representation of a single piece of scalar state in a Stateful; for use
187  *  with types that can be written to / read from stringstreams.
188  */
189 template<class T>
190 class Property : public PropertyTemplate<T>
191 {
192 public:
193         Property (PropertyDescriptor<T> q, T const& v)
194                 : PropertyTemplate<T> (q, v)
195         {}
196
197         Property (PropertyDescriptor<T> q, T const& o, T const& c)
198                 : PropertyTemplate<T> (q, o, c)
199         {}
200
201         Property<T>* clone () const {
202                 return new Property<T> (*this);
203         }
204         
205         Property<T>* clone_from_xml (const XMLNode& node) const {
206                 XMLNodeList const & children = node.children ();
207                 XMLNodeList::const_iterator i = children.begin();
208                 while (i != children.end() && (*i)->name() != this->property_name()) {
209                         ++i;
210                 }
211
212                 if (i == children.end()) {
213                         return 0;
214                 }
215                 XMLProperty* from = (*i)->property ("from");
216                 XMLProperty* to = (*i)->property ("to");
217                                 
218                 if (!from || !to) {
219                         return 0;
220                 }
221                         
222                 return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ()));
223         }
224
225         T & operator=(T const& v) {
226                 this->set (v);
227                 return this->_current;
228         }
229
230 private:
231         friend class PropertyFactory;
232
233         Property (PropertyDescriptor<T> q)
234                 : PropertyTemplate<T> (q)
235         {}
236
237         /* Note that we do not set a locale for the streams used
238          * in to_string() or from_string(), because we want the
239          * format to be portable across locales (i.e. C or
240          * POSIX). Also, there is the small matter of
241          * std::locale aborting on OS X if used with anything
242          * other than C or POSIX locales.
243          */
244         virtual std::string to_string (T const& v) const {
245                 std::stringstream s;
246                 s.precision (12); // in case its floating point
247                 s << v;
248                 return s.str ();
249         }
250
251         virtual T from_string (std::string const& s) const {
252                 std::stringstream t (s);
253                 T                 v;
254                 t >> v;
255                 return v;
256         }
257
258 };
259
260 /** Specialization, for std::string which is common and special (see to_string() and from_string()
261  *  Using stringstream to read from a std::string is easy to get wrong because of whitespace
262  *  separators, etc.
263  */
264 template<>
265 class Property<std::string> : public PropertyTemplate<std::string>
266 {
267 public:
268         Property (PropertyDescriptor<std::string> q, std::string const& v)
269                 : PropertyTemplate<std::string> (q, v)
270         {}
271
272         Property<std::string>* clone () const {
273                 return new Property<std::string> (*this);
274         }
275         
276         std::string & operator=(std::string const& v) {
277                 this->set (v);
278                 return this->_current;
279         }
280
281 private:
282         std::string to_string (std::string const& v) const {
283                 return _current;
284         }
285
286         std::string from_string (std::string const& s) const {
287                 return s;
288         }
289
290 };
291
292 template<class T>
293 class EnumProperty : public Property<T>
294 {
295 public:
296         EnumProperty (PropertyDescriptor<T> q, T const& v)
297                 : Property<T> (q, v)
298         {}
299
300         T & operator=(T const& v) {
301                 this->set (v);
302                 return this->_current;
303         }
304
305 private:
306         std::string to_string (T const & v) const {
307                 return enum_2_string (v);
308         }
309
310         T from_string (std::string const & s) const {
311                 return static_cast<T> (string_2_enum (s, this->_current));
312         }
313 };
314         
315 } /* namespace PBD */
316
317 #include "pbd/property_list_impl.h"
318 #include "pbd/property_basics_impl.h"
319
320 #endif /* __pbd_properties_h__ */