Move ARDOUR::Change into PBD so that Stateful can be aware 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
28 class XMLNode;
29
30 namespace PBD {
31
32 namespace sys {
33         class path;
34 }
35
36 enum Change {
37         range_guarantee = ~0
38 };
39
40 Change new_change ();
41
42 /** Base (non template) part of State */        
43 class StateBase
44 {
45 public:
46         StateBase (std::string const & p, Change c)
47                 : _have_old (false)
48                 , _xml_property_name (p)
49                 , _change (c)
50         {
51
52         }
53
54         StateBase (StateBase const & s)
55                 : _have_old (s._have_old)
56                 , _xml_property_name (s._xml_property_name)
57                 , _change (s._change)
58         {
59
60         }
61
62         /** Forget about any old value for this state */
63         void clear_history () {
64                 _have_old = false;
65         }
66
67         virtual void diff (XMLNode *, XMLNode *) const = 0;
68         virtual Change set_state (XMLNode const &) = 0;
69         virtual void add_state (XMLNode &) const = 0;
70
71 protected:
72         bool _have_old;
73         std::string _xml_property_name;
74         Change _change;
75 };
76
77 /** Class to represent a single piece of state in a Stateful object */
78 template <class T>
79 class State : public StateBase
80 {
81 public:
82         State (std::string const & p, Change c, T const & v)
83                 : StateBase (p, c)
84                 , _current (v)
85         {
86
87         }
88
89         State (State<T> const & s)
90                 : StateBase (s)
91         {
92                 _current = s._current;
93                 _old = s._old;
94         }               
95
96         State<T> & operator= (State<T> const & s) {
97                 /* XXX: isn't there a nicer place to do this? */
98                 _have_old = s._have_old;
99                 _xml_property_name = s._xml_property_name;
100                 _change = s._change;
101                 
102                 _current = s._current;
103                 _old = s._old;
104                 return *this;
105         }
106
107         T & operator= (T const & v) {
108                 set (v);
109                 return _current;
110         }
111
112         T & operator+= (T const & v) {
113                 set (_current + v);
114                 return _current;
115         }
116
117         operator T () const {
118                 return _current;
119         }
120         
121         T const & get () const {
122                 return _current;
123         }
124
125         void diff (XMLNode* old, XMLNode* current) const {
126                 if (_have_old) {
127                         old->add_property (_xml_property_name.c_str(), to_string (_old));
128                         current->add_property (_xml_property_name.c_str(), to_string (_current));
129                 }
130         }
131
132         /** Try to set state from the property of an XML node.
133          *  @param node XML node.
134          *  @return Change effected, or 0.
135          */
136         Change set_state (XMLNode const & node) {
137                 XMLProperty const * p = node.property (_xml_property_name.c_str());
138
139                 if (p) {
140                         std::stringstream s (p->value ());
141                         T v;
142                         s >> v;
143
144                         if (v == _current) {
145                                 return Change (0);
146                         }
147
148                         set (v);
149                         return _change;
150                 }
151
152                 return Change (0);
153         }
154
155         void add_state (XMLNode & node) const {
156                 node.add_property (_xml_property_name.c_str(), to_string (_current));
157         }
158
159 private:
160         void set (T const & v) {
161                 _old = _current;
162                 _have_old = true;
163                 _current = v;
164         }
165
166         std::string to_string (T const & v) const {
167                 std::stringstream s;
168                 s << v;
169                 return s.str ();
170         }
171                 
172         T _current;
173         T _old;
174 };
175
176 /** Base class for objects with saveable and undoable state */
177 class Stateful {
178   public:
179         Stateful ();
180         virtual ~Stateful();
181
182         virtual XMLNode& get_state (void) = 0;
183
184         virtual int set_state (const XMLNode&, int version) = 0;
185
186         void add_state (StateBase & s) {
187                 _states.push_back (&s);
188         }
189
190         /* Extra XML nodes */
191
192         void add_extra_xml (XMLNode&);
193         XMLNode *extra_xml (const std::string& str);
194
195         const PBD::ID& id() const { return _id; }
196
197         void clear_history ();
198         std::pair<XMLNode *, XMLNode*> diff ();
199
200         static int current_state_version;
201         static int loading_state_version;
202
203   protected:
204
205         void add_instant_xml (XMLNode&, const sys::path& directory_path);
206         XMLNode *instant_xml (const std::string& str, const sys::path& directory_path);
207         Change set_state_using_states (XMLNode const &);
208         void add_states (XMLNode &);
209
210         XMLNode *_extra_xml;
211         XMLNode *_instant_xml;
212         PBD::ID _id;
213
214         std::string _xml_node_name; ///< name of node to use for this object in XML
215         std::list<StateBase*> _states; ///< state variables that this object has
216 };
217
218 } // namespace PBD
219
220 #endif /* __pbd_stateful_h__ */
221