Merge branch 'master' into windows
[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 #ifdef COMPILER_MSVC
22 #include <io.h>      // Microsoft's nearest equivalent to <unistd.h>
23 #else
24 #include <unistd.h>
25 #endif
26
27 #include <glibmm/fileutils.h>
28 #include <glibmm/miscutils.h>
29
30 #include "pbd/debug.h"
31 #include "pbd/stateful.h"
32 #include "pbd/property_list.h"
33 #include "pbd/properties.h"
34 #include "pbd/destructible.h"
35 #include "pbd/xml++.h"
36 #include "pbd/error.h"
37
38 #include "i18n.h"
39
40 using namespace std;
41
42 namespace PBD {
43
44 int Stateful::current_state_version = 0;
45 int Stateful::loading_state_version = 0;
46
47 Stateful::Stateful ()
48         : _properties (new OwnedPropertyList)
49         , _stateful_frozen (0)
50 {
51         _extra_xml = 0;
52         _instant_xml = 0;
53 }
54
55 Stateful::~Stateful ()
56 {
57         delete _properties;
58
59         // Do not delete _extra_xml.  The use of add_child_nocopy() 
60         // means it needs to live on indefinately.
61
62         delete _instant_xml;
63 }
64
65 void
66 Stateful::add_extra_xml (XMLNode& node)
67 {
68         if (_extra_xml == 0) {
69                 _extra_xml = new XMLNode ("Extra");
70         }
71
72         _extra_xml->remove_nodes (node.name());
73         _extra_xml->add_child_nocopy (node);
74 }
75
76 XMLNode *
77 Stateful::extra_xml (const string& str, bool add_if_missing)
78 {
79         XMLNode* node = 0;
80
81         if (_extra_xml) {
82                 node = _extra_xml->child (str.c_str());
83         }
84
85         if (!node && add_if_missing) {
86                 node = new XMLNode (str);
87                 add_extra_xml (*node);
88         } 
89
90         return node;
91 }
92
93 void
94 Stateful::save_extra_xml (const XMLNode& node)
95 {
96         /* Looks for the child node called "Extra" and makes _extra_xml 
97            point to a copy of it. Will delete any existing node pointed
98            to by _extra_xml if a new Extra node is found, but not
99            otherwise.
100         */
101         
102         const XMLNode* xtra = node.child ("Extra");
103
104         if (xtra) {
105                 delete _extra_xml;
106                 _extra_xml = new XMLNode (*xtra);
107         }
108 }
109
110 void
111 Stateful::add_instant_xml (XMLNode& node, const std::string& directory_path)
112 {
113         if (!Glib::file_test (directory_path, Glib::FILE_TEST_IS_DIR)) {
114                 if (g_mkdir_with_parents (directory_path.c_str(), 0755) != 0) {
115                         error << string_compose(_("Error: could not create directory %1"), directory_path) << endmsg;
116                         return;
117                 }
118         }
119
120         if (_instant_xml == 0) {
121                 _instant_xml = new XMLNode ("instant");
122         }
123
124         _instant_xml->remove_nodes_and_delete (node.name());
125         _instant_xml->add_child_copy (node);
126
127         std::string instant_xml_path = Glib::build_filename (directory_path, "instant.xml");
128         
129         XMLTree tree;
130         tree.set_filename(instant_xml_path);
131
132         /* Important: the destructor for an XMLTree deletes
133            all of its nodes, starting at _root. We therefore
134            cannot simply hand it our persistent _instant_xml 
135            node as its _root, because we will lose it whenever
136            the Tree goes out of scope.
137
138            So instead, copy the _instant_xml node (which does 
139            a deep copy), and hand that to the tree.
140         */
141
142         XMLNode* copy = new XMLNode (*_instant_xml);
143         tree.set_root (copy);
144
145         if (!tree.write()) {
146                 error << string_compose(_("Error: could not write %1"), instant_xml_path) << endmsg;
147         }
148 }
149
150 XMLNode *
151 Stateful::instant_xml (const string& str, const std::string& directory_path)
152 {
153         if (_instant_xml == 0) {
154
155                 std::string instant_xml_path = Glib::build_filename (directory_path, "instant.xml");
156
157                 if (Glib::file_test (instant_xml_path, Glib::FILE_TEST_EXISTS)) {
158                         XMLTree tree;
159                         if (tree.read(instant_xml_path)) {
160                                 _instant_xml = new XMLNode(*(tree.root()));
161                         } else {
162                                 warning << string_compose(_("Could not understand XML file %1"), instant_xml_path) << endmsg;
163                                 return 0;
164                         }
165                 } else {
166                         return 0;
167                 }
168         }
169
170         const XMLNodeList& nlist = _instant_xml->children();
171         XMLNodeConstIterator i;
172
173         for (i = nlist.begin(); i != nlist.end(); ++i) {
174                 if ((*i)->name() == str) {
175                         return (*i);
176                 }
177         }
178
179         return 0;
180 }
181
182 /** Forget about any changes to this object's properties */
183 void
184 Stateful::clear_changes ()
185 {
186         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
187                 i->second->clear_changes ();
188         }
189 }
190
191 PropertyList *
192 Stateful::get_changes_as_properties (Command* cmd) const
193 {
194         PropertyList* pl = new PropertyList;
195         
196         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
197                 i->second->get_changes_as_properties (*pl, cmd);
198         }
199
200         return pl;
201 }
202
203 /** Set our property values from an XML node.
204  *  Derived types can call this from set_state() (or elsewhere)
205  *  to get basic property setting done.
206  *  @return IDs of properties that were changed.
207  */
208 PropertyChange
209 Stateful::set_values (XMLNode const & node)
210 {
211         PropertyChange c;
212         
213         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
214                 if (i->second->set_value (node)) {
215                         c.add (i->first);
216                 }
217         }
218
219         post_set (c);
220
221         return c;
222 }
223
224 PropertyChange
225 Stateful::apply_changes (const PropertyList& property_list)
226 {
227         PropertyChange c;
228         PropertyList::const_iterator p;
229
230         DEBUG_TRACE (DEBUG::Stateful, string_compose ("Stateful %1 setting properties from list of %2\n", this, property_list.size()));
231
232         for (PropertyList::const_iterator pp = property_list.begin(); pp != property_list.end(); ++pp) {
233                 DEBUG_TRACE (DEBUG::Stateful, string_compose ("in plist: %1\n", pp->second->property_name()));
234         }
235         
236         for (PropertyList::const_iterator i = property_list.begin(); i != property_list.end(); ++i) {
237                 if ((p = _properties->find (i->first)) != _properties->end()) {
238
239                         DEBUG_TRACE (
240                                 DEBUG::Stateful,
241                                 string_compose ("actually setting property %1 using %2\n", p->second->property_name(), i->second->property_name())
242                                 );
243                         
244                         if (apply_changes (*i->second)) {
245                                 c.add (i->first);
246                         }
247                 } else {
248                         DEBUG_TRACE (DEBUG::Stateful, string_compose ("passed in property %1 not found in own property list\n",
249                                                                       i->second->property_name()));
250                 }
251         }
252         
253         post_set (c);
254
255         send_change (c);
256
257         return c;
258 }
259
260 /** Add property states to an XML node.
261  *  @param owner_state Node.
262  */
263 void
264 Stateful::add_properties (XMLNode& owner_state)
265 {
266         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
267                 i->second->get_value (owner_state);
268         }
269 }
270
271 void
272 Stateful::add_property (PropertyBase& s)
273 {
274         _properties->add (s);
275 }
276
277 void
278 Stateful::send_change (const PropertyChange& what_changed)
279 {
280         if (what_changed.empty()) {
281                 return;
282         }
283
284         {
285                 Glib::Threads::Mutex::Lock lm (_lock);
286                 if (property_changes_suspended ()) {
287                         _pending_changed.add (what_changed);
288                         return;
289                 }
290         }
291
292         PropertyChanged (what_changed);
293 }
294
295 void
296 Stateful::suspend_property_changes ()
297 {
298         g_atomic_int_add (&_stateful_frozen, 1);
299 }
300
301 void
302 Stateful::resume_property_changes ()
303 {
304         PropertyChange what_changed;
305
306         {
307                 Glib::Threads::Mutex::Lock lm (_lock);
308
309                 if (property_changes_suspended() && g_atomic_int_dec_and_test (&_stateful_frozen) == FALSE) {
310                         return;
311                 }
312
313                 if (!_pending_changed.empty()) {
314                         what_changed = _pending_changed;
315                         _pending_changed.clear ();
316                 }
317         }
318
319         mid_thaw (what_changed);
320
321         send_change (what_changed);
322 }
323
324 bool
325 Stateful::changed() const  
326 {
327         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
328                 if (i->second->changed()) {
329                         return true;
330                 }
331         }
332
333         return false;
334 }
335
336 bool
337 Stateful::apply_changes (const PropertyBase& prop)
338 {
339         OwnedPropertyList::iterator i = _properties->find (prop.property_id());
340         if (i == _properties->end()) {
341                 return false;
342         }
343
344         i->second->apply_changes (&prop);
345         return true;
346 }
347
348 PropertyList*
349 Stateful::property_factory (const XMLNode& history_node) const
350 {
351         PropertyList* prop_list = new PropertyList;
352
353         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
354                 PropertyBase* prop = i->second->clone_from_xml (history_node);
355
356                 if (prop) {
357                         prop_list->add (prop);
358                 }
359         }
360
361         return prop_list;
362 }
363
364 void
365 Stateful::rdiff (vector<Command*>& cmds) const
366 {
367         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
368                 i->second->rdiff (cmds);
369         }
370 }
371
372 void
373 Stateful::clear_owned_changes ()
374 {
375         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
376                 i->second->clear_owned_changes ();
377         }
378 }
379   
380 bool
381 Stateful::set_id (const XMLNode& node) 
382 {
383         const XMLProperty* prop;
384
385         if ((prop = node.property ("id")) != 0) {
386                 _id = prop->value ();
387                 return true;
388         } 
389
390         return false;
391 }
392
393 void
394 Stateful::reset_id ()
395 {
396         _id = ID ();
397 }
398
399 void
400 Stateful::set_id (const string& str)
401 {
402         _id = str;
403 }
404
405 } // namespace PBD