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