1 /*
2  * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2010-2016 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2010 David Robillard <d@drobilla.net>
5  * Copyright (C) 2014-2015 Robin Gareus <robin@gareus.org>
6  * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #ifndef __pbd_properties_h__
24 #define __pbd_properties_h__
25 
26 #include <string>
27 #include <list>
28 #include <set>
29 
30 #include "pbd/libpbd_visibility.h"
31 #include "pbd/xml++.h"
32 #include "pbd/property_basics.h"
33 #include "pbd/property_list.h"
34 #include "pbd/enumwriter.h"
35 #include "pbd/stateful.h"
36 #include "pbd/string_convert.h"
37 
38 namespace PBD {
39 
40 /** Parent class for classes which represent a single scalar property in a Stateful object */
41 template<class T>
42 class /*LIBPBD_API*/ PropertyTemplate : public PropertyBase
43 {
44 public:
PropertyTemplate(PropertyDescriptor<T> p,T const & v)45 	PropertyTemplate (PropertyDescriptor<T> p, T const& v)
46 		: PropertyBase (p.property_id)
47 		, _have_old (false)
48 		, _current (v)
49 	{}
50 
PropertyTemplate(PropertyDescriptor<T> p,T const & o,T const & c)51 	PropertyTemplate (PropertyDescriptor<T> p, T const& o, T const& c)
52 		: PropertyBase (p.property_id)
53 		, _have_old (true)
54 		, _current (c)
55 		, _old (o)
56 	{}
57 
PropertyTemplate(PropertyDescriptor<T> p,PropertyTemplate<T> const & s)58 	PropertyTemplate (PropertyDescriptor<T> p, PropertyTemplate<T> const & s)
59 		: PropertyBase (p.property_id)
60 		, _have_old (false)
61 		, _current (s._current)
62 	{}
63 
64 
65 	/* OPERATORS / ACCESSORS */
66 
67 	T & operator=(T const& v) {
68 		set (v);
69 		return _current;
70 	}
71 
72 	/* This will mean that, if fred and jim are both PropertyTemplates,
73 	 * fred = jim will result in fred taking on jim's current value,
74 	 * but NOT jim's property ID.
75 	 */
76 	PropertyTemplate<T> & operator= (PropertyTemplate<T> const & p) {
77 		set (p._current);
78 		return *this;
79 	}
80 
81 	T & operator+=(T const& v) {
82 		set (_current + v);
83 		return _current;
84 	}
85 
86 	bool operator==(const T& other) const {
87 		return _current == other;
88 	}
89 
90 	bool operator!=(const T& other) const {
91 		return _current != other;
92 	}
93 
94 	operator T const &() const {
95 		return _current;
96 	}
97 
val()98 	T const& val () const {
99 		return _current;
100 	}
101 
102 
103 	/* MANAGEMENT OF Stateful State */
104 
set_value(XMLNode const & node)105 	bool set_value (XMLNode const & node) {
106 
107 		XMLProperty const* p = node.property (property_name());
108 
109 		if (p) {
110 			T const v = from_string (p->value ());
111 
112 			if (v != _current) {
113 				set (v);
114 				return true;
115 			}
116 		}
117 
118 		return false;
119 	}
120 
get_value(XMLNode & node)121 	void get_value (XMLNode & node) const {
122 		node.set_property (property_name (), _current);
123 	}
124 
125 
126 	/* MANAGEMENT OF HISTORY */
127 
clear_changes()128 	void clear_changes () {
129 		_have_old = false;
130 	}
131 
changed()132 	bool changed () const { return _have_old; }
133 
invert()134 	void invert () {
135 		T const tmp = _current;
136 		_current = _old;
137 		_old = tmp;
138 	}
139 
140 
141 	/* TRANSFERRING HISTORY TO / FROM A StatefulDiffCommand */
142 
get_changes_as_xml(XMLNode * history_node)143 	void get_changes_as_xml (XMLNode* history_node) const {
144 		XMLNode* node = history_node->add_child (property_name());
145 		node->set_property ("from", _old);
146 		node->set_property ("to", _current);
147 	}
148 
get_changes_as_properties(PropertyList & changes,Command *)149 	void get_changes_as_properties (PropertyList& changes, Command *) const {
150 		if (this->_have_old) {
151 			changes.add (clone ());
152 		}
153 	}
154 
155 
156 	/* VARIOUS */
157 
apply_changes(PropertyBase const * p)158 	void apply_changes (PropertyBase const * p) {
159 		T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val ();
160 		if (v != _current) {
161 			set (v);
162 		}
163 	}
164 
165 protected:
166 
set(T const & v)167 	void set (T const& v) {
168 		if (v != _current) {
169 			if (!_have_old) {
170 				_old = _current;
171 				_have_old = true;
172 			} else {
173 				if (v == _old) {
174 					/* value has been reset to the value
175 					   at the start of a history transaction,
176 					   before clear_changes() is called.
177 					   thus there is effectively no apparent
178 					   history for this property.
179 					*/
180 					_have_old = false;
181 				}
182 			}
183 
184 			_current  = v;
185 		}
186 	}
187 
188 	virtual std::string to_string (T const& v) const             = 0;
189 	virtual T           from_string (std::string const& s) const = 0;
190 
191 	bool _have_old;
192 	T _current;
193 	T _old;
194 
195 private:
196 	/* disallow copy-construction; it's not obvious whether it should mean
197 	   a copy of just the value, or the value and property ID.
198 	*/
199 	PropertyTemplate (PropertyTemplate<T> const &);
200 };
201 
202 template<class T> /*LIBPBD_API*/
203 std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
204 {
205 	return os << s.val ();
206 }
207 
208 /** Representation of a single piece of scalar state in a Stateful; for use
209  *  with types that can be written to / read from stringstreams.
210  */
211 template<class T>
212 class /*LIBPBD_API*/ Property : public PropertyTemplate<T>
213 {
214 public:
Property(PropertyDescriptor<T> q,T const & v)215 	Property (PropertyDescriptor<T> q, T const& v)
216 		: PropertyTemplate<T> (q, v)
217 	{}
218 
Property(PropertyDescriptor<T> q,T const & o,T const & c)219 	Property (PropertyDescriptor<T> q, T const& o, T const& c)
220 		: PropertyTemplate<T> (q, o, c)
221 	{}
222 
Property(PropertyDescriptor<T> q,Property<T> const & v)223 	Property (PropertyDescriptor<T> q, Property<T> const& v)
224 		: PropertyTemplate<T> (q, v)
225 	{}
226 
clone()227 	Property<T>* clone () const {
228 		return new Property<T> (this->property_id(), this->_old, this->_current);
229 	}
230 
clone_from_xml(const XMLNode & node)231 	Property<T>* clone_from_xml (const XMLNode& node) const {
232 		XMLNodeList const & children = node.children ();
233 		XMLNodeList::const_iterator i = children.begin();
234 		while (i != children.end() && (*i)->name() != this->property_name()) {
235 			++i;
236 		}
237 
238 		if (i == children.end()) {
239 			return 0;
240 		}
241 		XMLProperty const * from = (*i)->property ("from");
242 		XMLProperty const * to = (*i)->property ("to");
243 
244 		if (!from || !to) {
245 			return 0;
246 		}
247 
248 		return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ()));
249 	}
250 
251 	T & operator=(T const& v) {
252 		this->set (v);
253 		return this->_current;
254 	}
255 
256 	Property<T>& operator=(Property<T> const& v) {
257 		this->set (v);
258 		return *this;
259 	}
260 
261 private:
262         friend class PropertyFactory;
263 
264 	/* no copy-construction */
265 	Property (Property<T> const &);
266 
267 	/* Note that we do not set a locale for the streams used
268 	 * in to_string() or from_string(), because we want the
269 	 * format to be portable across locales (i.e. C or
270 	 * POSIX). Also, there is the small matter of
271 	 * std::locale aborting on OS X if used with anything
272 	 * other than C or POSIX locales.
273 	 */
to_string(T const & v)274 	virtual std::string to_string (T const& v) const {
275 		return PBD::to_string (v);
276 	}
277 
from_string(std::string const & s)278 	virtual T from_string (std::string const& s) const {
279 		return PBD::string_to<T>(s);
280 	}
281 
282 };
283 
284 /** Specialization, for std::string which is common and special (see to_string() and from_string()
285  *  Using stringstream to read from a std::string is easy to get wrong because of whitespace
286  *  separators, etc.
287  */
288 template<>
289 class /*LIBPBD_API*/ Property<std::string> : public PropertyTemplate<std::string>
290 {
291 public:
Property(PropertyDescriptor<std::string> d,std::string const & v)292 	Property (PropertyDescriptor<std::string> d, std::string const & v)
293 		: PropertyTemplate<std::string> (d, v)
294 	{}
295 
Property(PropertyDescriptor<std::string> d,std::string const & o,std::string const & c)296 	Property (PropertyDescriptor<std::string> d, std::string const & o, std::string const & c)
297 		: PropertyTemplate<std::string> (d, o, c)
298 	{}
299 
clone()300 	Property<std::string>* clone () const {
301 		return new Property<std::string> (this->property_id(), _old, _current);
302 	}
303 
304 	std::string & operator= (std::string const& v) {
305 		this->set (v);
306 		return this->_current;
307 	}
308 
309 private:
to_string(std::string const & v)310 	std::string to_string (std::string const& v) const {
311 		return v;
312 	}
313 
from_string(std::string const & s)314 	std::string from_string (std::string const& s) const {
315 		return s;
316 	}
317 
318 	/* no copy-construction */
319 	Property (Property<std::string> const &);
320 };
321 
322 template<class T>
323 class /*LIBPBD_API*/ EnumProperty : public Property<T>
324 {
325 public:
EnumProperty(PropertyDescriptor<T> q,T const & v)326 	EnumProperty (PropertyDescriptor<T> q, T const& v)
327 		: Property<T> (q, v)
328 	{}
329 
330 	T & operator=(T const& v) {
331 		this->set (v);
332 		return this->_current;
333 	}
334 
335 private:
to_string(T const & v)336 	std::string to_string (T const & v) const {
337 		return enum_2_string (v);
338 	}
339 
from_string(std::string const & s)340 	T from_string (std::string const & s) const {
341 		return static_cast<T> (string_2_enum (s, this->_current));
342 	}
343 
344 	/* no copy-construction */
345 	EnumProperty (EnumProperty const &);
346 	EnumProperty& operator= (EnumProperty const &);
347 };
348 
349 /** A Property which holds a shared_ptr to a Stateful object,
350  *  and handles undo using the somewhat inefficient approach
351  *  of saving the complete XML state of its object before and
352  *  after changes.  A sort of half-way house between the old
353  *  complete-state undo system and the new difference-based
354  *  one.
355  */
356 template <class T>
357 class /*LIBPBD_API*/ SharedStatefulProperty : public PropertyBase
358 {
359 public:
360 	typedef boost::shared_ptr<T> Ptr;
361 
SharedStatefulProperty(PropertyID d,Ptr p)362 	SharedStatefulProperty (PropertyID d, Ptr p)
363 		: PropertyBase (d)
364 		, _current (p)
365 	{
366 
367 	}
368 
SharedStatefulProperty(PropertyID d,Ptr o,Ptr c)369 	SharedStatefulProperty (PropertyID d, Ptr o, Ptr c)
370 		: PropertyBase (d)
371 		, _old (o)
372 		, _current (c)
373 	{
374 
375 	}
376 
set_value(XMLNode const & node)377 	bool set_value (XMLNode const & node) {
378 
379 		/* Look for our node */
380 		XMLNode* n = node.child (property_name ());
381 		if (!n) {
382 			return false;
383 		}
384 
385 		/* And there should be one child which is the state of our T */
386 		XMLNodeList const & children = n->children ();
387 		if (children.size() != 1) {
388 			return false;
389 		}
390 
391 		_current->set_state (*children.front (), Stateful::current_state_version);
392 		return true;
393 	}
394 
get_value(XMLNode & node)395 	void get_value (XMLNode & node) const {
396 		XMLNode* n = node.add_child (property_name ());
397 		n->add_child_nocopy (_current->get_state ());
398 	}
399 
clear_changes()400 	void clear_changes () {
401 		/* We are starting to change things, so _old gets set up
402 		   with the current state.
403 		*/
404 		_old.reset (new T (*_current.get()));
405 	}
406 
changed()407 	bool changed () const {
408 		if (!_old) {
409 			return false;
410 		}
411 		/* Expensive, but, hey; this requires operator!= in our T */
412 		return (*_old != *_current);
413 	}
414 
invert()415 	void invert () {
416 		_current.swap (_old);
417 	}
418 
get_changes_as_xml(XMLNode * history_node)419 	void get_changes_as_xml (XMLNode* history_node) const {
420 		/* We express the diff as before and after state, just
421 		   as MementoCommand does.
422 		*/
423 		XMLNode* p = history_node->add_child (property_name ());
424 		XMLNode* from = p->add_child ("from");
425 		from->add_child_nocopy (_old->get_state ());
426 		XMLNode* to = p->add_child ("to");
427 		to->add_child_nocopy (_current->get_state ());
428 	}
429 
get_changes_as_properties(PropertyList & changes,Command *)430 	void get_changes_as_properties (PropertyList& changes, Command *) const {
431 		if (changed ()) {
432 			changes.add (clone ());
433 		}
434 	}
435 
apply_changes(PropertyBase const * p)436 	void apply_changes (PropertyBase const * p) {
437 		*_current = *(dynamic_cast<SharedStatefulProperty const *> (p))->val ();
438 	}
439 
val()440 	Ptr val () const {
441 		return _current;
442 	}
443 
444 	T* operator-> () const {
445 		return _current.operator-> ();
446 	}
447 
448 	operator bool () const {
449 		return _current ? true : false;
450 	}
451 
452 protected:
453 
454 	Ptr _old;
455 	Ptr _current;
456 
457 private:
458 
459 	/* No copy-construction nor assignment */
460 	SharedStatefulProperty (SharedStatefulProperty<T> const &);
461 	SharedStatefulProperty<T>& operator= (SharedStatefulProperty<T> const &);
462 };
463 
464 } /* namespace PBD */
465 
466 #include "pbd/property_list_impl.h"
467 #include "pbd/property_basics_impl.h"
468 
469 #endif /* __pbd_properties_h__ */
470