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