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
Stateful()53 Stateful::Stateful ()
54 : _extra_xml (0)
55 , _instant_xml (0)
56 , _properties (new OwnedPropertyList)
57 {
58 g_atomic_int_set (&_stateful_frozen, 0);
59 }
60
~Stateful()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
add_extra_xml(XMLNode & node)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 *
extra_xml(const string & str,bool add_if_missing)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
save_extra_xml(const XMLNode & node)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
add_instant_xml(XMLNode & node,const std::string & directory_path)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 *
instant_xml(const string & str,const std::string & directory_path)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
clear_changes()190 Stateful::clear_changes ()
191 {
192 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
193 i->second->clear_changes ();
194 }
195 _pending_changed.clear ();
196 }
197
198 PropertyList *
get_changes_as_properties(Command * cmd) const199 Stateful::get_changes_as_properties (Command* cmd) const
200 {
201 PropertyList* pl = new PropertyList;
202
203 for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
204 i->second->get_changes_as_properties (*pl, cmd);
205 }
206
207 return pl;
208 }
209
210 /** Set our property values from an XML node.
211 * Derived types can call this from set_state() (or elsewhere)
212 * to get basic property setting done.
213 * @return IDs of properties that were changed.
214 */
215 PropertyChange
set_values(XMLNode const & node)216 Stateful::set_values (XMLNode const & node)
217 {
218 PropertyChange c;
219
220 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
221 if (i->second->set_value (node)) {
222 c.add (i->first);
223 }
224 }
225
226 post_set (c);
227
228 return c;
229 }
230
231 PropertyChange
apply_changes(const PropertyList & property_list)232 Stateful::apply_changes (const PropertyList& property_list)
233 {
234 PropertyChange c;
235 PropertyList::const_iterator p;
236
237 DEBUG_TRACE (DEBUG::Stateful, string_compose ("Stateful %1 setting properties from list of %2\n", this, property_list.size()));
238
239 for (PropertyList::const_iterator pp = property_list.begin(); pp != property_list.end(); ++pp) {
240 DEBUG_TRACE (DEBUG::Stateful, string_compose ("in plist: %1\n", pp->second->property_name()));
241 }
242
243 for (PropertyList::const_iterator i = property_list.begin(); i != property_list.end(); ++i) {
244 if ((p = _properties->find (i->first)) != _properties->end()) {
245
246 DEBUG_TRACE (
247 DEBUG::Stateful,
248 string_compose ("actually setting property %1 using %2\n", p->second->property_name(), i->second->property_name())
249 );
250
251 if (apply_changes (*i->second)) {
252 c.add (i->first);
253 }
254 } else {
255 DEBUG_TRACE (DEBUG::Stateful, string_compose ("passed in property %1 not found in own property list\n",
256 i->second->property_name()));
257 }
258 }
259
260 post_set (c);
261
262 send_change (c);
263
264 return c;
265 }
266
267 /** Add property states to an XML node.
268 * @param owner_state Node.
269 */
270 void
add_properties(XMLNode & owner_state)271 Stateful::add_properties (XMLNode& owner_state)
272 {
273 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
274 i->second->get_value (owner_state);
275 }
276 }
277
278 void
add_property(PropertyBase & s)279 Stateful::add_property (PropertyBase& s)
280 {
281 _properties->add (s);
282 }
283
284 void
send_change(const PropertyChange & what_changed)285 Stateful::send_change (const PropertyChange& what_changed)
286 {
287 if (what_changed.empty()) {
288 return;
289 }
290
291 {
292 Glib::Threads::Mutex::Lock lm (_lock);
293 if (property_changes_suspended ()) {
294 _pending_changed.add (what_changed);
295 return;
296 }
297 }
298
299 PropertyChanged (what_changed);
300 }
301
302 void
suspend_property_changes()303 Stateful::suspend_property_changes ()
304 {
305 g_atomic_int_add (&_stateful_frozen, 1);
306 }
307
308 void
resume_property_changes()309 Stateful::resume_property_changes ()
310 {
311 PropertyChange what_changed;
312
313 {
314 Glib::Threads::Mutex::Lock lm (_lock);
315
316 if (property_changes_suspended() && g_atomic_int_dec_and_test (&_stateful_frozen) == FALSE) {
317 return;
318 }
319
320 if (!_pending_changed.empty()) {
321 what_changed = _pending_changed;
322 _pending_changed.clear ();
323 }
324 }
325
326 mid_thaw (what_changed);
327
328 send_change (what_changed);
329 }
330
331 bool
changed() const332 Stateful::changed() const
333 {
334 for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
335 if (i->second->changed()) {
336 return true;
337 }
338 }
339
340 return false;
341 }
342
343 bool
apply_changes(const PropertyBase & prop)344 Stateful::apply_changes (const PropertyBase& prop)
345 {
346 OwnedPropertyList::iterator i = _properties->find (prop.property_id());
347 if (i == _properties->end()) {
348 return false;
349 }
350
351 i->second->apply_changes (&prop);
352 return true;
353 }
354
355 PropertyList*
property_factory(const XMLNode & history_node) const356 Stateful::property_factory (const XMLNode& history_node) const
357 {
358 PropertyList* prop_list = new PropertyList;
359
360 for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
361 PropertyBase* prop = i->second->clone_from_xml (history_node);
362
363 if (prop) {
364 prop_list->add (prop);
365 }
366 }
367
368 return prop_list;
369 }
370
371 void
rdiff(vector<Command * > & cmds) const372 Stateful::rdiff (vector<Command*>& cmds) const
373 {
374 for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
375 i->second->rdiff (cmds);
376 }
377 }
378
379 void
clear_owned_changes()380 Stateful::clear_owned_changes ()
381 {
382 for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
383 i->second->clear_owned_changes ();
384 }
385 }
386
387 bool
set_id(const XMLNode & node)388 Stateful::set_id (const XMLNode& node)
389 {
390 bool* regen = _regenerate_xml_or_string_ids.get();
391
392 if (regen && *regen) {
393 reset_id ();
394 return true;
395 }
396
397 if (node.get_property ("id", _id)) {
398 return true;
399 }
400
401 return false;
402 }
403
404 void
reset_id()405 Stateful::reset_id ()
406 {
407 _id = ID ();
408 }
409
410 void
set_id(const string & str)411 Stateful::set_id (const string& str)
412 {
413 bool* regen = _regenerate_xml_or_string_ids.get();
414
415 if (regen && *regen) {
416 reset_id ();
417 } else {
418 _id = str;
419 }
420 }
421
422 bool
regenerate_xml_or_string_ids() const423 Stateful::regenerate_xml_or_string_ids () const
424 {
425 bool* regen = _regenerate_xml_or_string_ids.get();
426 if (regen && *regen) {
427 return true;
428 } else {
429 return false;
430 }
431 }
432
433 void
set_regenerate_xml_and_string_ids_in_this_thread(bool yn)434 Stateful::set_regenerate_xml_and_string_ids_in_this_thread (bool yn)
435 {
436 bool* val = new bool (yn);
437 _regenerate_xml_or_string_ids.set (val);
438 }
439
440 } // namespace PBD
441