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