a50c54d7f53e8261972e636f9880b3c798fa516a
[ardour.git] / libs / pbd / stateful.cc
1 /*
2     Copyright (C) 2000-2001 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     $Id: stateful.cc 629 2006-06-21 23:01:03Z paul $
19 */
20
21 #include <unistd.h>
22
23 #include "pbd/stateful.h"
24 #include "pbd/destructible.h"
25 #include "pbd/filesystem.h"
26 #include "pbd/xml++.h"
27 #include "pbd/error.h"
28
29 #include "i18n.h"
30
31 using namespace std;
32
33 namespace PBD {
34
35 int Stateful::current_state_version = 0;
36 int Stateful::loading_state_version = 0;
37
38 PBD::Change
39 new_change ()
40 {
41         Change c;
42         static uint32_t change_bit = 1;
43
44         /* catch out-of-range */
45         if (!change_bit)
46         {
47                 fatal << _("programming error: ")
48                         << "change_bit out of range in ARDOUR::new_change()"
49                         << endmsg;
50                 /*NOTREACHED*/
51         }
52
53         c = Change (change_bit);
54         change_bit <<= 1;       // if it shifts too far, change_bit == 0
55
56         return c;
57 }
58
59 Stateful::Stateful ()
60 {
61         _extra_xml = 0;
62         _instant_xml = 0;
63 }
64
65 Stateful::~Stateful ()
66 {
67         // Do not delete _extra_xml.  The use of add_child_nocopy() 
68         // means it needs to live on indefinately.
69
70         delete _instant_xml;
71 }
72
73 void
74 Stateful::add_extra_xml (XMLNode& node)
75 {
76         if (_extra_xml == 0) {
77                 _extra_xml = new XMLNode ("Extra");
78         }
79
80         _extra_xml->remove_nodes (node.name());
81         _extra_xml->add_child_nocopy (node);
82 }
83
84 XMLNode *
85 Stateful::extra_xml (const string& str)
86 {
87         if (_extra_xml == 0) {
88                 return 0;
89         }
90
91         const XMLNodeList& nlist = _extra_xml->children();
92         XMLNodeConstIterator i;
93
94         for (i = nlist.begin(); i != nlist.end(); ++i) {
95                 if ((*i)->name() == str) {
96                         return (*i);
97                 }
98         }
99
100         return 0;
101 }
102
103 void
104 Stateful::add_instant_xml (XMLNode& node, const sys::path& directory_path)
105 {
106         sys::create_directories (directory_path); // may throw
107
108         if (_instant_xml == 0) {
109                 _instant_xml = new XMLNode ("instant");
110         }
111
112         _instant_xml->remove_nodes_and_delete (node.name());
113         _instant_xml->add_child_copy (node);
114
115         sys::path instant_xml_path(directory_path);
116
117         instant_xml_path /= "instant.xml";
118         
119         XMLTree tree;
120         tree.set_filename(instant_xml_path.to_string());
121
122         /* Important: the destructor for an XMLTree deletes
123            all of its nodes, starting at _root. We therefore
124            cannot simply hand it our persistent _instant_xml 
125            node as its _root, because we will lose it whenever
126            the Tree goes out of scope.
127
128            So instead, copy the _instant_xml node (which does 
129            a deep copy), and hand that to the tree.
130         */
131
132         XMLNode* copy = new XMLNode (*_instant_xml);
133         tree.set_root (copy);
134
135         if (!tree.write()) {
136                 error << string_compose(_("Error: could not write %1"), instant_xml_path.to_string()) << endmsg;
137         }
138 }
139
140 XMLNode *
141 Stateful::instant_xml (const string& str, const sys::path& directory_path)
142 {
143         if (_instant_xml == 0) {
144
145                 sys::path instant_xml_path(directory_path);
146                 instant_xml_path /= "instant.xml";
147
148                 if (exists(instant_xml_path)) {
149                         XMLTree tree;
150                         if (tree.read(instant_xml_path.to_string())) {
151                                 _instant_xml = new XMLNode(*(tree.root()));
152                         } else {
153                                 warning << string_compose(_("Could not understand XML file %1"), instant_xml_path.to_string()) << endmsg;
154                                 return 0;
155                         }
156                 } else {
157                         return 0;
158                 }
159         }
160
161         const XMLNodeList& nlist = _instant_xml->children();
162         XMLNodeConstIterator i;
163
164         for (i = nlist.begin(); i != nlist.end(); ++i) {
165                 if ((*i)->name() == str) {
166                         return (*i);
167                 }
168         }
169
170         return 0;
171 }
172
173 /** Forget about any old state for this object */       
174 void
175 Stateful::clear_history ()
176 {
177         for (list<StateBase*>::iterator i = _states.begin(); i != _states.end(); ++i) {
178                 (*i)->clear_history ();
179         }
180 }
181
182 /** @return A pair of XMLNodes representing state that has changed since the last time clear_history
183  *  was called on this object; the first is the state before, the second the state after.
184  *
185  *  It is the caller's responsibility to delete the returned XMLNodes.
186  */
187 pair<XMLNode *, XMLNode *>
188 Stateful::diff ()
189 {
190         XMLNode* old = new XMLNode (_xml_node_name);
191         XMLNode* current = new XMLNode (_xml_node_name);
192
193         for (list<StateBase*>::iterator i = _states.begin(); i != _states.end(); ++i) {
194                 (*i)->diff (old, current);
195         }
196
197         return make_pair (old, current);
198 }
199         
200 /** Set state of _states from an XML node.
201  *  @param node Node.
202  *  @return Changes made.
203  */
204 Change
205 Stateful::set_state_using_states (XMLNode const & node)
206 {
207         Change c = Change (0);
208         
209         for (list<StateBase*>::iterator i = _states.begin(); i != _states.end(); ++i) {
210                 c = Change (c | (*i)->set_state (node));
211         }
212
213         return c;
214 }
215
216 /** Add state of _states to an XML node.
217  *  @param node Node.
218  */
219 void
220 Stateful::add_states (XMLNode & node)
221 {
222         for (list<StateBase*>::iterator i = _states.begin(); i != _states.end(); ++i) {
223                 (*i)->add_state (node);
224         }
225 }
226
227 } // namespace PBD