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