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