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