Remove invalid error message
[ardour.git] / libs / pbd / stateful.cc
1 /*
2  * Copyright (C) 2000-2016 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2006 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2007-2016 Tim Mayberry <mojofunk@gmail.com>
5  * Copyright (C) 2008-2011 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2009-2010 David Robillard <d@drobilla.net>
7  * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #ifdef COMPILER_MSVC
25 #include <io.h>      // Microsoft's nearest equivalent to <unistd.h>
26 #else
27 #include <unistd.h>
28 #endif
29
30 #include <glibmm/fileutils.h>
31 #include <glibmm/miscutils.h>
32
33 #include "pbd/debug.h"
34 #include "pbd/stateful.h"
35 #include "pbd/types_convert.h"
36 #include "pbd/property_list.h"
37 #include "pbd/properties.h"
38 #include "pbd/destructible.h"
39 #include "pbd/xml++.h"
40 #include "pbd/error.h"
41
42 #include "pbd/i18n.h"
43
44 using namespace std;
45
46 namespace PBD {
47
48 int Stateful::current_state_version = 0;
49 int Stateful::loading_state_version = 0;
50
51 Glib::Threads::Private<bool> Stateful::_regenerate_xml_or_string_ids;
52
53 Stateful::Stateful ()
54         : _extra_xml (0)
55         , _instant_xml (0)
56         , _properties (new OwnedPropertyList)
57         , _stateful_frozen (0)
58 {
59 }
60
61 Stateful::~Stateful ()
62 {
63         delete _properties;
64
65         // Do not delete _extra_xml.  The use of add_child_nocopy()
66         // means it needs to live on indefinately.
67
68         delete _instant_xml;
69 }
70
71 void
72 Stateful::add_extra_xml (XMLNode& node)
73 {
74         if (_extra_xml == 0) {
75                 _extra_xml = new XMLNode ("Extra");
76         }
77
78         _extra_xml->remove_nodes_and_delete (node.name());
79         _extra_xml->add_child_nocopy (node);
80 }
81
82 XMLNode *
83 Stateful::extra_xml (const string& str, bool add_if_missing)
84 {
85         XMLNode* node = 0;
86
87         if (_extra_xml) {
88                 node = _extra_xml->child (str.c_str());
89         }
90
91         if (!node && add_if_missing) {
92                 node = new XMLNode (str);
93                 add_extra_xml (*node);
94         }
95
96         return node;
97 }
98
99 void
100 Stateful::save_extra_xml (const XMLNode& node)
101 {
102         /* Looks for the child node called "Extra" and makes _extra_xml
103            point to a copy of it. Will delete any existing node pointed
104            to by _extra_xml if a new Extra node is found, but not
105            otherwise.
106         */
107
108         const XMLNode* xtra = node.child ("Extra");
109
110         if (xtra) {
111                 delete _extra_xml;
112                 _extra_xml = new XMLNode (*xtra);
113         }
114 }
115
116 void
117 Stateful::add_instant_xml (XMLNode& node, const std::string& directory_path)
118 {
119         if (!Glib::file_test (directory_path, Glib::FILE_TEST_IS_DIR)) {
120                 if (g_mkdir_with_parents (directory_path.c_str(), 0755) != 0) {
121                         error << string_compose(_("Error: could not create directory %1"), directory_path) << endmsg;
122                         return;
123                 }
124         }
125
126         if (_instant_xml == 0) {
127                 _instant_xml = new XMLNode ("instant");
128         }
129
130         _instant_xml->remove_nodes_and_delete (node.name());
131         _instant_xml->add_child_copy (node);
132
133         std::string instant_xml_path = Glib::build_filename (directory_path, "instant.xml");
134
135         XMLTree tree;
136         tree.set_filename(instant_xml_path);
137
138         /* Important: the destructor for an XMLTree deletes
139            all of its nodes, starting at _root. We therefore
140            cannot simply hand it our persistent _instant_xml
141            node as its _root, because we will lose it whenever
142            the Tree goes out of scope.
143
144            So instead, copy the _instant_xml node (which does
145            a deep copy), and hand that to the tree.
146         */
147
148         XMLNode* copy = new XMLNode (*_instant_xml);
149         tree.set_root (copy);
150
151         if (!tree.write()) {
152                 error << string_compose(_("Error: could not write %1"), instant_xml_path) << endmsg;
153         }
154 }
155
156 XMLNode *
157 Stateful::instant_xml (const string& str, const std::string& directory_path)
158 {
159         if (_instant_xml == 0) {
160
161                 std::string instant_xml_path = Glib::build_filename (directory_path, "instant.xml");
162
163                 if (Glib::file_test (instant_xml_path, Glib::FILE_TEST_EXISTS)) {
164                         XMLTree tree;
165                         if (tree.read(instant_xml_path)) {
166                                 _instant_xml = new XMLNode(*(tree.root()));
167                         } else {
168                                 warning << string_compose(_("Could not understand XML file %1"), instant_xml_path) << endmsg;
169                                 return 0;
170                         }
171                 } else {
172                         return 0;
173                 }
174         }
175
176         const XMLNodeList& nlist = _instant_xml->children();
177         XMLNodeConstIterator i;
178
179         for (i = nlist.begin(); i != nlist.end(); ++i) {
180                 if ((*i)->name() == str) {
181                         return (*i);
182                 }
183         }
184
185         return 0;
186 }
187
188 /** Forget about any changes to this object's properties */
189 void
190 Stateful::clear_changes ()
191 {
192         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
193                 i->second->clear_changes ();
194         }
195 }
196
197 PropertyList *
198 Stateful::get_changes_as_properties (Command* cmd) const
199 {
200         PropertyList* pl = new PropertyList;
201
202         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
203                 i->second->get_changes_as_properties (*pl, cmd);
204         }
205
206         return pl;
207 }
208
209 /** Set our property values from an XML node.
210  *  Derived types can call this from set_state() (or elsewhere)
211  *  to get basic property setting done.
212  *  @return IDs of properties that were changed.
213  */
214 PropertyChange
215 Stateful::set_values (XMLNode const & node)
216 {
217         PropertyChange c;
218
219         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
220                 if (i->second->set_value (node)) {
221                         c.add (i->first);
222                 }
223         }
224
225         post_set (c);
226
227         return c;
228 }
229
230 PropertyChange
231 Stateful::apply_changes (const PropertyList& property_list)
232 {
233         PropertyChange c;
234         PropertyList::const_iterator p;
235
236         DEBUG_TRACE (DEBUG::Stateful, string_compose ("Stateful %1 setting properties from list of %2\n", this, property_list.size()));
237
238         for (PropertyList::const_iterator pp = property_list.begin(); pp != property_list.end(); ++pp) {
239                 DEBUG_TRACE (DEBUG::Stateful, string_compose ("in plist: %1\n", pp->second->property_name()));
240         }
241
242         for (PropertyList::const_iterator i = property_list.begin(); i != property_list.end(); ++i) {
243                 if ((p = _properties->find (i->first)) != _properties->end()) {
244
245                         DEBUG_TRACE (
246                                 DEBUG::Stateful,
247                                 string_compose ("actually setting property %1 using %2\n", p->second->property_name(), i->second->property_name())
248                                 );
249
250                         if (apply_changes (*i->second)) {
251                                 c.add (i->first);
252                         }
253                 } else {
254                         DEBUG_TRACE (DEBUG::Stateful, string_compose ("passed in property %1 not found in own property list\n",
255                                                                       i->second->property_name()));
256                 }
257         }
258
259         post_set (c);
260
261         send_change (c);
262
263         return c;
264 }
265
266 /** Add property states to an XML node.
267  *  @param owner_state Node.
268  */
269 void
270 Stateful::add_properties (XMLNode& owner_state)
271 {
272         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
273                 i->second->get_value (owner_state);
274         }
275 }
276
277 void
278 Stateful::add_property (PropertyBase& s)
279 {
280         _properties->add (s);
281 }
282
283 void
284 Stateful::send_change (const PropertyChange& what_changed)
285 {
286         if (what_changed.empty()) {
287                 return;
288         }
289
290         {
291                 Glib::Threads::Mutex::Lock lm (_lock);
292                 if (property_changes_suspended ()) {
293                         _pending_changed.add (what_changed);
294                         return;
295                 }
296         }
297
298         PropertyChanged (what_changed);
299 }
300
301 void
302 Stateful::suspend_property_changes ()
303 {
304         g_atomic_int_add (&_stateful_frozen, 1);
305 }
306
307 void
308 Stateful::resume_property_changes ()
309 {
310         PropertyChange what_changed;
311
312         {
313                 Glib::Threads::Mutex::Lock lm (_lock);
314
315                 if (property_changes_suspended() && g_atomic_int_dec_and_test (&_stateful_frozen) == FALSE) {
316                         return;
317                 }
318
319                 if (!_pending_changed.empty()) {
320                         what_changed = _pending_changed;
321                         _pending_changed.clear ();
322                 }
323         }
324
325         mid_thaw (what_changed);
326
327         send_change (what_changed);
328 }
329
330 bool
331 Stateful::changed() const
332 {
333         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
334                 if (i->second->changed()) {
335                         return true;
336                 }
337         }
338
339         return false;
340 }
341
342 bool
343 Stateful::apply_changes (const PropertyBase& prop)
344 {
345         OwnedPropertyList::iterator i = _properties->find (prop.property_id());
346         if (i == _properties->end()) {
347                 return false;
348         }
349
350         i->second->apply_changes (&prop);
351         return true;
352 }
353
354 PropertyList*
355 Stateful::property_factory (const XMLNode& history_node) const
356 {
357         PropertyList* prop_list = new PropertyList;
358
359         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
360                 PropertyBase* prop = i->second->clone_from_xml (history_node);
361
362                 if (prop) {
363                         prop_list->add (prop);
364                 }
365         }
366
367         return prop_list;
368 }
369
370 void
371 Stateful::rdiff (vector<Command*>& cmds) const
372 {
373         for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
374                 i->second->rdiff (cmds);
375         }
376 }
377
378 void
379 Stateful::clear_owned_changes ()
380 {
381         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
382                 i->second->clear_owned_changes ();
383         }
384 }
385
386 bool
387 Stateful::set_id (const XMLNode& node)
388 {
389         bool* regen = _regenerate_xml_or_string_ids.get();
390
391         if (regen && *regen) {
392                 reset_id ();
393                 return true;
394         }
395
396         if (node.get_property ("id", _id)) {
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 bool
422 Stateful::regenerate_xml_or_string_ids () const
423 {
424         bool* regen = _regenerate_xml_or_string_ids.get();
425         if (regen && *regen) {
426                 return true;
427         } else {
428                 return false;
429         }
430 }
431
432 void
433 Stateful::set_regenerate_xml_and_string_ids_in_this_thread (bool yn)
434 {
435         bool* val = new bool (yn);
436         _regenerate_xml_or_string_ids.set (val);
437 }
438
439 } // namespace PBD