More tinkering with State<>. Use some StateDiffCommands instead of
[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== (std::string const & o) const {
104                 return o == to_string (_current);
105         }
106
107         bool operator!= (std::string const & o) const {
108                 return o != to_string (_current);
109         }
110
111         operator T const & () const {
112                 return _current;
113         }
114
115         T const & get () 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.get();
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 << v;
194                 return s.str ();
195         }
196
197         T from_string (std::string const & s) const {
198                 std::stringstream t (s);
199                 T v;
200                 t >> v;
201                 return v;
202         }
203 };
204
205 template <class T>
206 class EnumState : public StateTemplate<T>
207 {
208 public:
209         EnumState (std::string const & p, Change c, T const & v)
210                 : StateTemplate<T> (p, c, v)
211         {
212
213         }
214         
215         T & operator= (T const & v) {
216                 this->set (v);
217                 return this->_current;
218         }
219
220 private:
221         std::string to_string (T const & v) const {
222                 return enum_2_string (v);
223         }
224
225         T from_string (std::string const & v) const {
226                 return T (string_2_enum (v, this->_current));
227         }
228 };
229
230 /** Base class for objects with saveable and undoable state */
231 class Stateful {
232   public:
233         Stateful ();
234         virtual ~Stateful();
235
236         virtual XMLNode& get_state (void) = 0;
237
238         virtual int set_state (const XMLNode&, int version) = 0;
239
240         void add_state (StateBase & s) {
241                 _states.push_back (&s);
242         }
243
244         /* Extra XML nodes */
245
246         void add_extra_xml (XMLNode&);
247         XMLNode *extra_xml (const std::string& str);
248
249         const PBD::ID& id() const { return _id; }
250
251         void clear_history ();
252         std::pair<XMLNode *, XMLNode*> diff ();
253
254         static int current_state_version;
255         static int loading_state_version;
256
257   protected:
258
259         void add_instant_xml (XMLNode&, const sys::path& directory_path);
260         XMLNode *instant_xml (const std::string& str, const sys::path& directory_path);
261         Change set_state_using_states (XMLNode const &);
262         void add_states (XMLNode &);
263
264         XMLNode *_extra_xml;
265         XMLNode *_instant_xml;
266         PBD::ID _id;
267
268         std::string _xml_node_name; ///< name of node to use for this object in XML
269         std::list<StateBase*> _states; ///< state variables that this object has
270 };
271
272 } // namespace PBD
273
274 #endif /* __pbd_stateful_h__ */
275