new file
[ardour.git] / libs / pbd / pbd / stateful.h
1 /*
2     Copyright (C) 2000-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_stateful_h__
21 #define __pbd_stateful_h__
22
23 #include <string>
24 #include <cassert>
25 #include "pbd/id.h"
26 #include "pbd/xml++.h"
27 #include "pbd/enumwriter.h"
28
29 class XMLNode;
30
31 namespace PBD {
32
33 namespace sys {
34         class path;
35 }
36
37 enum Change {
38         range_guarantee = ~0
39 };
40
41 Change new_change ();
42
43 /** Base (non template) part of State */        
44 class StateBase
45 {
46 public:
47         StateBase (std::string const & p, Change c)
48                 : _have_old (false)
49                 , _xml_property_name (p)
50                 , _change (c)
51         {
52
53         }
54
55         /** Forget about any old value for this state */
56         void clear_history () {
57                 _have_old = false;
58         }
59
60         virtual void diff (XMLNode *, XMLNode *) const = 0;
61         virtual Change set_state (XMLNode const &) = 0;
62         virtual void add_state (XMLNode &) const = 0;
63
64 protected:
65         bool _have_old;
66         std::string _xml_property_name;
67         Change _change;
68 };
69
70 /** Parent class for classes which represent a single piece of state in a Stateful object */
71 template <class T>
72 class StateTemplate : public StateBase
73 {
74 public:
75         StateTemplate (std::string const & p, Change c, T const & v)
76                 : StateBase (p, c)
77                 , _current (v)
78         {
79
80         }
81
82         StateTemplate<T> & operator= (StateTemplate<T> const & s) {
83                 /* XXX: isn't there a nicer place to do this? */
84                 _have_old = s._have_old;
85                 _xml_property_name = s._xml_property_name;
86                 _change = s._change;
87                 
88                 _current = s._current;
89                 _old = s._old;
90                 return *this;
91         }
92
93         T & operator= (T const & v) {
94                 set (v);
95                 return _current;
96         }
97
98         T & operator+= (T const & v) {
99                 set (_current + v);
100                 return _current;
101         }
102         
103         bool operator== (const T& other) const {
104                 return _current == other;
105         }
106
107         bool operator!= (const T& other) const {
108                 return _current != other;
109         }
110
111         operator T const & () const {
112                 return _current;
113         }
114
115         T const & val () const {
116                 return _current;
117         }
118
119         void diff (XMLNode* old, XMLNode* current) const {
120                 if (_have_old) {
121                         old->add_property (_xml_property_name.c_str(), to_string (_old));
122                         current->add_property (_xml_property_name.c_str(), to_string (_current));
123                 }
124         }
125
126         /** Try to set state from the property of an XML node.
127          *  @param node XML node.
128          *  @return Change effected, or 0.
129          */
130         Change set_state (XMLNode const & node) {
131                 XMLProperty const * p = node.property (_xml_property_name.c_str());
132
133                 if (p) {
134                         T const v = from_string (p->value ());
135
136                         if (v == _current) {
137                                 return Change (0);
138                         }
139
140                         set (v);
141                         return _change;
142                 }
143
144                 return Change (0);
145         }
146
147         void add_state (XMLNode & node) const {
148                 node.add_property (_xml_property_name.c_str(), to_string (_current));
149         }
150
151 protected:
152         void set (T const & v) {
153                 _old = _current;
154                 _have_old = true;
155                 _current = v;
156         }
157
158         virtual std::string to_string (T const & v) const = 0;
159         virtual T from_string (std::string const & s) const = 0;
160                 
161         T _current;
162         T _old;
163 };
164
165 template<class T>       
166 std::ostream& operator<< (std::ostream& os, StateTemplate<T> const & s)
167 {
168         os << s.val();
169         return os;
170 }
171
172 /** Representation of a single piece of state in a Stateful; for use
173  *  with types that can be written to / read from stringstreams.
174  */
175 template <class T>
176 class State : public StateTemplate<T>
177 {
178 public:
179         State (std::string const & p, Change c, T const & v)
180                 : StateTemplate<T> (p, c, v)
181         {
182
183         }
184         
185         T & operator= (T const & v) {
186                 this->set (v);
187                 return this->_current;
188         }
189         
190 private:        
191         std::string to_string (T const & v) const {
192                 std::stringstream s;
193                 s.precision (12); // in case its floating point
194                 s << v;
195                 return s.str ();
196         }
197
198         T from_string (std::string const & s) const {
199                 std::stringstream t (s);
200                 T v;
201                 t.precision (12); // in case its floating point
202                 t >> v;
203                 return v;
204         }
205 };
206
207 template <class T>
208 class EnumState : public StateTemplate<T>
209 {
210 public:
211         EnumState (std::string const & p, Change c, T const & v)
212                 : StateTemplate<T> (p, c, v)
213         {
214
215         }
216         
217         T & operator= (T const & v) {
218                 this->set (v);
219                 return this->_current;
220         }
221
222 private:
223         std::string to_string (T const & v) const {
224                 return enum_2_string (v);
225         }
226
227         T from_string (std::string const & v) const {
228                 return T (string_2_enum (v, this->_current));
229         }
230 };
231
232 /** Base class for objects with saveable and undoable state */
233 class Stateful {
234   public:
235         Stateful ();
236         virtual ~Stateful();
237
238         virtual XMLNode& get_state (void) = 0;
239
240         virtual int set_state (const XMLNode&, int version) = 0;
241
242         void add_state (StateBase & s) {
243                 _states.push_back (&s);
244         }
245
246         /* Extra XML nodes */
247
248         void add_extra_xml (XMLNode&);
249         XMLNode *extra_xml (const std::string& str);
250
251         const PBD::ID& id() const { return _id; }
252
253         void clear_history ();
254         std::pair<XMLNode *, XMLNode*> diff ();
255
256         static int current_state_version;
257         static int loading_state_version;
258
259   protected:
260
261         void add_instant_xml (XMLNode&, const sys::path& directory_path);
262         XMLNode *instant_xml (const std::string& str, const sys::path& directory_path);
263         Change set_state_using_states (XMLNode const &);
264         void add_states (XMLNode &);
265
266         XMLNode *_extra_xml;
267         XMLNode *_instant_xml;
268         PBD::ID _id;
269
270         std::string _xml_node_name; ///< name of node to use for this object in XML
271         std::list<StateBase*> _states; ///< state variables that this object has
272 };
273
274 } // namespace PBD
275
276 #endif /* __pbd_stateful_h__ */
277