Merge branch 'master' into cairocanvas
[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 #include "pbd/stateful.h"
34
35 namespace PBD {
36
37 /** Parent class for classes which represent a single scalar property in a Stateful object */
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
62         /* OPERATORS / ACCESSORS */
63
64         T & operator=(T const& v) {
65                 set (v);
66                 return _current;
67         }
68
69         /* This will mean that, if fred and jim are both PropertyTemplates,
70          * fred = jim will result in fred taking on jim's current value,
71          * but NOT jim's property ID.
72          */
73         PropertyTemplate<T> & operator= (PropertyTemplate<T> const & p) {
74                 set (p._current);
75                 return *this;
76         }
77
78         T & operator+=(T const& v) {
79                 set (_current + v);
80                 return _current;
81         }
82
83         bool operator==(const T& other) const {
84                 return _current == other;
85         }
86
87         bool operator!=(const T& other) const {
88                 return _current != other;
89         }
90
91         operator T const &() const {
92                 return _current;
93         }
94
95         T const& val () const {
96                 return _current;
97         }
98
99
100         /* MANAGEMENT OF Stateful State */
101         
102         bool set_value (XMLNode const & node) {
103
104                 XMLProperty const* p = node.property (property_name());
105
106                 if (p) {
107                         T const v = from_string (p->value ());
108
109                         if (v != _current) {
110                                 set (v);
111                                 return true;
112                         }
113                 }
114
115                 return false;
116         }
117
118         void get_value (XMLNode & node) const {
119                 node.add_property (property_name(), to_string (_current));
120         }
121
122         
123         /* MANAGEMENT OF HISTORY */
124         
125         void clear_changes () {
126                 _have_old = false;
127         }
128
129         bool changed () const { return _have_old; }
130
131         void invert () {
132                 T const tmp = _current;
133                 _current = _old;
134                 _old = tmp;
135         }
136
137
138         /* TRANSFERRING HISTORY TO / FROM A StatefulDiffCommand */
139         
140         void get_changes_as_xml (XMLNode* history_node) const {
141                 XMLNode* node = history_node->add_child (property_name());
142                 node->add_property ("from", to_string (_old));
143                 node->add_property ("to", to_string (_current));
144         }
145
146         void get_changes_as_properties (PropertyList& changes, Command *) const {
147                 if (this->_have_old) {
148                         changes.add (clone ());
149                 }
150         }
151
152
153         /* VARIOUS */
154
155         void apply_changes (PropertyBase const * p) {
156                 T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val ();
157                 if (v != _current) {
158                         set (v);
159                 }
160         }
161
162 protected:
163
164         void set (T const& v) {
165                 if (v != _current) {
166                         if (!_have_old) {
167                                 _old = _current;
168                                 _have_old = true;
169                         } else {
170                                 if (v == _old) {
171                                         /* value has been reset to the value
172                                            at the start of a history transaction,
173                                            before clear_changes() is called.
174                                            thus there is effectively no apparent
175                                            history for this property.
176                                         */
177                                         _have_old = false;
178                                 }
179                         }
180
181                         _current  = v;
182                 } 
183         }
184
185         virtual std::string to_string (T const& v) const             = 0;
186         virtual T           from_string (std::string const& s) const = 0;
187
188         bool _have_old;
189         T _current;
190         T _old;
191
192 private:
193         /* disallow copy-construction; it's not obvious whether it should mean
194            a copy of just the value, or the value and property ID.
195         */
196         PropertyTemplate (PropertyTemplate<T> const &);
197 };
198
199 template<class T>
200 std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
201 {
202         return os << s.val ();
203 }
204
205 /** Representation of a single piece of scalar state in a Stateful; for use
206  *  with types that can be written to / read from stringstreams.
207  */
208 template<class T>
209 class Property : public PropertyTemplate<T>
210 {
211 public:
212         Property (PropertyDescriptor<T> q, T const& v)
213                 : PropertyTemplate<T> (q, v)
214         {}
215
216         Property (PropertyDescriptor<T> q, T const& o, T const& c)
217                 : PropertyTemplate<T> (q, o, c)
218         {}
219
220         Property (PropertyDescriptor<T> q, Property<T> const& v)
221                 : PropertyTemplate<T> (q, v)
222         {}
223
224         Property<T>* clone () const {
225                 return new Property<T> (this->property_id(), this->_old, this->_current);
226         }
227         
228         Property<T>* clone_from_xml (const XMLNode& node) const {
229                 XMLNodeList const & children = node.children ();
230                 XMLNodeList::const_iterator i = children.begin();
231                 while (i != children.end() && (*i)->name() != this->property_name()) {
232                         ++i;
233                 }
234
235                 if (i == children.end()) {
236                         return 0;
237                 }
238                 XMLProperty* from = (*i)->property ("from");
239                 XMLProperty* to = (*i)->property ("to");
240                                 
241                 if (!from || !to) {
242                         return 0;
243                 }
244                         
245                 return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ()));
246         }
247
248         T & operator=(T const& v) {
249                 this->set (v);
250                 return this->_current;
251         }
252
253 private:
254         friend class PropertyFactory;
255
256         /* no copy-construction */
257         Property (Property<T> const &);
258
259         /* Note that we do not set a locale for the streams used
260          * in to_string() or from_string(), because we want the
261          * format to be portable across locales (i.e. C or
262          * POSIX). Also, there is the small matter of
263          * std::locale aborting on OS X if used with anything
264          * other than C or POSIX locales.
265          */
266         virtual std::string to_string (T const& v) const {
267                 std::stringstream s;
268                 s.precision (12); // in case its floating point
269                 s << v;
270                 return s.str ();
271         }
272
273         virtual T from_string (std::string const& s) const {
274                 std::stringstream t (s);
275                 T                 v;
276                 t >> v;
277                 return v;
278         }
279
280 };
281
282 /** Specialization, for std::string which is common and special (see to_string() and from_string()
283  *  Using stringstream to read from a std::string is easy to get wrong because of whitespace
284  *  separators, etc.
285  */
286 template<>
287 class Property<std::string> : public PropertyTemplate<std::string>
288 {
289 public:
290         Property (PropertyDescriptor<std::string> d, std::string const & v)
291                 : PropertyTemplate<std::string> (d, v)
292         {}
293
294         Property (PropertyDescriptor<std::string> d, std::string const & o, std::string const & c)
295                 : PropertyTemplate<std::string> (d, o, c)
296         {}
297         
298         Property<std::string>* clone () const {
299                 return new Property<std::string> (this->property_id(), _old, _current);
300         }
301
302         std::string & operator= (std::string const& v) {
303                 this->set (v);
304                 return this->_current;
305         }
306
307 private:
308         std::string to_string (std::string const& v) const {
309                 return v;
310         }
311
312         std::string from_string (std::string const& s) const {
313                 return s;
314         }
315
316         /* no copy-construction */
317         Property (Property<std::string> const &);
318 };
319
320 template<class T>
321 class EnumProperty : public Property<T>
322 {
323 public:
324         EnumProperty (PropertyDescriptor<T> q, T const& v)
325                 : Property<T> (q, v)
326         {}
327
328         T & operator=(T const& v) {
329                 this->set (v);
330                 return this->_current;
331         }
332
333 private:
334         std::string to_string (T const & v) const {
335                 return enum_2_string (v);
336         }
337
338         T from_string (std::string const & s) const {
339                 return static_cast<T> (string_2_enum (s, this->_current));
340         }
341
342         /* no copy-construction */
343         EnumProperty (EnumProperty const &);
344 };
345
346 /** A Property which holds a shared_ptr to a Stateful object,
347  *  and handles undo using the somewhat inefficient approach
348  *  of saving the complete XML state of its object before and
349  *  after changes.  A sort of half-way house between the old
350  *  complete-state undo system and the new difference-based
351  *  one.
352  */
353 template <class T>
354 class SharedStatefulProperty : public PropertyBase
355 {
356 public:
357         typedef boost::shared_ptr<T> Ptr;
358         
359         SharedStatefulProperty (PropertyID d, Ptr p)
360                 : PropertyBase (d)
361                 , _current (p)
362         {
363                 
364         }
365
366         SharedStatefulProperty (PropertyID d, Ptr o, Ptr c)
367                 : PropertyBase (d)
368                 , _old (o)
369                 , _current (c)
370         {
371                 
372         }
373
374         bool set_value (XMLNode const & node) {
375
376                 /* Look for our node */
377                 XMLNode* n = node.child (property_name ());
378                 if (!n) {
379                         return false;
380                 }
381
382                 /* And there should be one child which is the state of our T */
383                 XMLNodeList const & children = n->children ();
384                 if (children.size() != 1) {
385                         return false;
386                 }
387
388                 _current->set_state (*children.front (), Stateful::current_state_version);
389                 return true;
390         }
391
392         void get_value (XMLNode & node) const {
393                 XMLNode* n = node.add_child (property_name ());
394                 n->add_child_nocopy (_current->get_state ());
395         }
396
397         void clear_changes () {
398                 /* We are starting to change things, so _old gets set up
399                    with the current state.
400                 */
401                 _old.reset (new T (*_current.get()));
402         }
403
404         bool changed () const {
405                 /* Expensive, but, hey; this requires operator!= in
406                    our T
407                 */
408                 return (*_old != *_current);
409         }
410
411         void invert () {
412                 _current.swap (_old);
413         }
414
415         void get_changes_as_xml (XMLNode* history_node) const {
416                 /* We express the diff as before and after state, just
417                    as MementoCommand does.
418                 */
419                 XMLNode* p = history_node->add_child (property_name ());
420                 XMLNode* from = p->add_child ("from");
421                 from->add_child_nocopy (_old->get_state ());
422                 XMLNode* to = p->add_child ("to");
423                 to->add_child_nocopy (_current->get_state ());
424         }
425
426         void get_changes_as_properties (PropertyList& changes, Command *) const {
427                 if (changed ()) {
428                         changes.add (clone ());
429                 }
430         }
431
432         void apply_changes (PropertyBase const * p) {
433                 *_current = *(dynamic_cast<SharedStatefulProperty const *> (p))->val ();
434         }
435
436         Ptr val () const {
437                 return _current;
438         }
439
440         T* operator-> () const {
441                 return _current.operator-> ();
442         }
443
444         operator bool () const {
445                 return _current;
446         }
447
448 protected:
449
450         Ptr _old;
451         Ptr _current;
452
453 private:
454
455         /* No copy-construction nor assignment */
456         SharedStatefulProperty (SharedStatefulProperty<T> const &);
457         SharedStatefulProperty<T>& operator= (SharedStatefulProperty<T> const &);
458 };
459
460 } /* namespace PBD */
461
462 #include "pbd/property_list_impl.h"
463 #include "pbd/property_basics_impl.h"
464
465 #endif /* __pbd_properties_h__ */