2 Copyright (C) 2010 Paul Davis
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.
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.
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.
20 #ifndef __pbd_properties_h__
21 #define __pbd_properties_h__
29 #include "pbd/libpbd_visibility.h"
30 #include "pbd/xml++.h"
31 #include "pbd/property_basics.h"
32 #include "pbd/property_list.h"
33 #include "pbd/enumwriter.h"
34 #include "pbd/stateful.h"
38 /** Parent class for classes which represent a single scalar property in a Stateful object */
40 class /*LIBPBD_API*/ PropertyTemplate : public PropertyBase
43 PropertyTemplate (PropertyDescriptor<T> p, T const& v)
44 : PropertyBase (p.property_id)
49 PropertyTemplate (PropertyDescriptor<T> p, T const& o, T const& c)
50 : PropertyBase (p.property_id)
56 PropertyTemplate (PropertyDescriptor<T> p, PropertyTemplate<T> const & s)
57 : PropertyBase (p.property_id)
59 , _current (s._current)
63 /* OPERATORS / ACCESSORS */
65 T & operator=(T const& v) {
70 /* This will mean that, if fred and jim are both PropertyTemplates,
71 * fred = jim will result in fred taking on jim's current value,
72 * but NOT jim's property ID.
74 PropertyTemplate<T> & operator= (PropertyTemplate<T> const & p) {
79 T & operator+=(T const& v) {
84 bool operator==(const T& other) const {
85 return _current == other;
88 bool operator!=(const T& other) const {
89 return _current != other;
92 operator T const &() const {
96 T const& val () const {
101 /* MANAGEMENT OF Stateful State */
103 bool set_value (XMLNode const & node) {
105 XMLProperty const* p = node.property (property_name());
108 T const v = from_string (p->value ());
119 void get_value (XMLNode & node) const {
120 node.add_property (property_name(), to_string (_current));
124 /* MANAGEMENT OF HISTORY */
126 void clear_changes () {
130 bool changed () const { return _have_old; }
133 T const tmp = _current;
139 /* TRANSFERRING HISTORY TO / FROM A StatefulDiffCommand */
141 void get_changes_as_xml (XMLNode* history_node) const {
142 XMLNode* node = history_node->add_child (property_name());
143 node->add_property ("from", to_string (_old));
144 node->add_property ("to", to_string (_current));
147 void get_changes_as_properties (PropertyList& changes, Command *) const {
148 if (this->_have_old) {
149 changes.add (clone ());
156 void apply_changes (PropertyBase const * p) {
157 T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val ();
165 void set (T const& v) {
172 /* value has been reset to the value
173 at the start of a history transaction,
174 before clear_changes() is called.
175 thus there is effectively no apparent
176 history for this property.
186 virtual std::string to_string (T const& v) const = 0;
187 virtual T from_string (std::string const& s) const = 0;
194 /* disallow copy-construction; it's not obvious whether it should mean
195 a copy of just the value, or the value and property ID.
197 PropertyTemplate (PropertyTemplate<T> const &);
200 template<class T> /*LIBPBD_API*/
201 std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
203 return os << s.val ();
206 /** Representation of a single piece of scalar state in a Stateful; for use
207 * with types that can be written to / read from stringstreams.
210 class /*LIBPBD_API*/ Property : public PropertyTemplate<T>
213 Property (PropertyDescriptor<T> q, T const& v)
214 : PropertyTemplate<T> (q, v)
217 Property (PropertyDescriptor<T> q, T const& o, T const& c)
218 : PropertyTemplate<T> (q, o, c)
221 Property (PropertyDescriptor<T> q, Property<T> const& v)
222 : PropertyTemplate<T> (q, v)
225 Property<T>* clone () const {
226 return new Property<T> (this->property_id(), this->_old, this->_current);
229 Property<T>* clone_from_xml (const XMLNode& node) const {
230 XMLNodeList const & children = node.children ();
231 XMLNodeList::const_iterator i = children.begin();
232 while (i != children.end() && (*i)->name() != this->property_name()) {
236 if (i == children.end()) {
239 XMLProperty* from = (*i)->property ("from");
240 XMLProperty* to = (*i)->property ("to");
246 return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ()));
249 T & operator=(T const& v) {
251 return this->_current;
255 friend class PropertyFactory;
257 /* no copy-construction */
258 Property (Property<T> const &);
260 /* Note that we do not set a locale for the streams used
261 * in to_string() or from_string(), because we want the
262 * format to be portable across locales (i.e. C or
263 * POSIX). Also, there is the small matter of
264 * std::locale aborting on OS X if used with anything
265 * other than C or POSIX locales.
267 virtual std::string to_string (T const& v) const {
269 s.precision (12); // in case its floating point
274 virtual T from_string (std::string const& s) const {
275 std::stringstream t (s);
283 /** Specialization, for std::string which is common and special (see to_string() and from_string()
284 * Using stringstream to read from a std::string is easy to get wrong because of whitespace
288 class /*LIBPBD_API*/ Property<std::string> : public PropertyTemplate<std::string>
291 Property (PropertyDescriptor<std::string> d, std::string const & v)
292 : PropertyTemplate<std::string> (d, v)
295 Property (PropertyDescriptor<std::string> d, std::string const & o, std::string const & c)
296 : PropertyTemplate<std::string> (d, o, c)
299 Property<std::string>* clone () const {
300 return new Property<std::string> (this->property_id(), _old, _current);
303 std::string & operator= (std::string const& v) {
305 return this->_current;
309 std::string to_string (std::string const& v) const {
313 std::string from_string (std::string const& s) const {
317 /* no copy-construction */
318 Property (Property<std::string> const &);
322 class /*LIBPBD_API*/ EnumProperty : public Property<T>
325 EnumProperty (PropertyDescriptor<T> q, T const& v)
329 T & operator=(T const& v) {
331 return this->_current;
335 std::string to_string (T const & v) const {
336 return enum_2_string (v);
339 T from_string (std::string const & s) const {
340 return static_cast<T> (string_2_enum (s, this->_current));
343 /* no copy-construction */
344 EnumProperty (EnumProperty const &);
347 /** A Property which holds a shared_ptr to a Stateful object,
348 * and handles undo using the somewhat inefficient approach
349 * of saving the complete XML state of its object before and
350 * after changes. A sort of half-way house between the old
351 * complete-state undo system and the new difference-based
355 class /*LIBPBD_API*/ SharedStatefulProperty : public PropertyBase
358 typedef boost::shared_ptr<T> Ptr;
360 SharedStatefulProperty (PropertyID d, Ptr p)
367 SharedStatefulProperty (PropertyID d, Ptr o, Ptr c)
375 bool set_value (XMLNode const & node) {
377 /* Look for our node */
378 XMLNode* n = node.child (property_name ());
383 /* And there should be one child which is the state of our T */
384 XMLNodeList const & children = n->children ();
385 if (children.size() != 1) {
389 _current->set_state (*children.front (), Stateful::current_state_version);
393 void get_value (XMLNode & node) const {
394 XMLNode* n = node.add_child (property_name ());
395 n->add_child_nocopy (_current->get_state ());
398 void clear_changes () {
399 /* We are starting to change things, so _old gets set up
400 with the current state.
402 _old.reset (new T (*_current.get()));
405 bool changed () const {
406 /* Expensive, but, hey; this requires operator!= in
409 return (*_old != *_current);
413 _current.swap (_old);
416 void get_changes_as_xml (XMLNode* history_node) const {
417 /* We express the diff as before and after state, just
418 as MementoCommand does.
420 XMLNode* p = history_node->add_child (property_name ());
421 XMLNode* from = p->add_child ("from");
422 from->add_child_nocopy (_old->get_state ());
423 XMLNode* to = p->add_child ("to");
424 to->add_child_nocopy (_current->get_state ());
427 void get_changes_as_properties (PropertyList& changes, Command *) const {
429 changes.add (clone ());
433 void apply_changes (PropertyBase const * p) {
434 *_current = *(dynamic_cast<SharedStatefulProperty const *> (p))->val ();
441 T* operator-> () const {
442 return _current.operator-> ();
445 operator bool () const {
456 /* No copy-construction nor assignment */
457 SharedStatefulProperty (SharedStatefulProperty<T> const &);
458 SharedStatefulProperty<T>& operator= (SharedStatefulProperty<T> const &);
461 } /* namespace PBD */
463 #include "pbd/property_list_impl.h"
464 #include "pbd/property_basics_impl.h"
466 #endif /* __pbd_properties_h__ */