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