Merge branch 'master' into cairocanvas
[ardour.git] / libs / pbd / pbd / properties.h
index 41a5f748810ffe79b26873b81a91a239a14e8ef4..70d18db4c7bd74468250b7fefe570bc324593bda 100644 (file)
 #include <set>
 #include <iostream>
 
+#include "pbd/libpbd_visibility.h"
 #include "pbd/xml++.h"
 #include "pbd/property_basics.h"
 #include "pbd/property_list.h"
 #include "pbd/enumwriter.h"
+#include "pbd/stateful.h"
 
 namespace PBD {
 
-/** Parent class for classes which represent a single scalar property in a Stateful object 
- */
+/** Parent class for classes which represent a single scalar property in a Stateful object */
 template<class T>
-class PropertyTemplate : public PropertyBase
+class /*LIBPBD_API*/ PropertyTemplate : public PropertyBase
 {
 public:
        PropertyTemplate (PropertyDescriptor<T> p, T const& v)
@@ -51,22 +52,30 @@ public:
                , _current (c)
                , _old (o)
        {}
-       
-       PropertyTemplate<T>& operator=(PropertyTemplate<T> const& s) {
-               /* XXX: isn't there a nicer place to do this? */
-               _have_old    = s._have_old;
-               _property_id = s._property_id;
 
-               _current = s._current;
-               _old     = s._old;
-               return *this;
-       }
+       PropertyTemplate (PropertyDescriptor<T> p, PropertyTemplate<T> const & s)
+               : PropertyBase (p.property_id)
+               , _have_old (false)
+               , _current (s._current)
+       {}
+
+
+       /* OPERATORS / ACCESSORS */
 
        T & operator=(T const& v) {
                set (v);
                return _current;
        }
 
+       /* This will mean that, if fred and jim are both PropertyTemplates,
+        * fred = jim will result in fred taking on jim's current value,
+        * but NOT jim's property ID.
+        */
+       PropertyTemplate<T> & operator= (PropertyTemplate<T> const & p) {
+               set (p._current);
+               return *this;
+       }
+
        T & operator+=(T const& v) {
                set (_current + v);
                return _current;
@@ -88,16 +97,9 @@ public:
                return _current;
        }
 
-       void clear_history () {
-               _have_old = false;
-       }
-
-       void get_changes_as_xml (XMLNode* history_node) const {
-               XMLNode* node = history_node->add_child (property_name());
-                node->add_property ("from", to_string (_old));
-                node->add_property ("to", to_string (_current));
-       }
 
+       /* MANAGEMENT OF Stateful State */
+       
        bool set_value (XMLNode const & node) {
 
                XMLProperty const* p = node.property (property_name());
@@ -118,51 +120,67 @@ public:
                 node.add_property (property_name(), to_string (_current));
        }
 
-       bool changed () const { return _have_old; }
        
-       void apply_changes (PropertyBase const * p) {
-               T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val ();
-               std::cout << "Apply changes: " << v << " cf " << _current << "\n";
-               if (v != _current) {
-                       set (v);
-               }
+       /* MANAGEMENT OF HISTORY */
+       
+       void clear_changes () {
+               _have_old = false;
        }
 
+       bool changed () const { return _have_old; }
+
        void invert () {
                T const tmp = _current;
                _current = _old;
                _old = tmp;
-               std::cout << "Inverted to " << _old << " -> " << _current << "\n";
        }
 
-protected:
-        /** Constructs a PropertyTemplate with a default
-            value for _old and _current.
-        */
 
-       PropertyTemplate (PropertyDescriptor<T> p)
-               : PropertyBase (p.property_id)
-       {}
+       /* TRANSFERRING HISTORY TO / FROM A StatefulDiffCommand */
+       
+       void get_changes_as_xml (XMLNode* history_node) const {
+               XMLNode* node = history_node->add_child (property_name());
+                node->add_property ("from", to_string (_old));
+                node->add_property ("to", to_string (_current));
+       }
+
+       void get_changes_as_properties (PropertyList& changes, Command *) const {
+               if (this->_have_old) {
+                       changes.add (clone ());
+               }
+       }
+
+
+       /* VARIOUS */
+
+       void apply_changes (PropertyBase const * p) {
+               T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val ();
+               if (v != _current) {
+                       set (v);
+               }
+       }
+
+protected:
 
        void set (T const& v) {
-                if (v != _current) {
-                        if (!_have_old) {
-                                _old = _current;
-                                _have_old = true;
-                        } else {
-                                if (v == _old) {
-                                        /* value has been reset to the value
-                                           at the start of a history transaction,
-                                           before clear_history() is called.
-                                           thus there is effectively no apparent
-                                           history for this property.
-                                        */
-                                        _have_old = false;
-                                }
-                        }
-
-                        _current  = v;
-                
+               if (v != _current) {
+                       if (!_have_old) {
+                               _old = _current;
+                               _have_old = true;
+                       } else {
+                               if (v == _old) {
+                                       /* value has been reset to the value
+                                          at the start of a history transaction,
+                                          before clear_changes() is called.
+                                          thus there is effectively no apparent
+                                          history for this property.
+                                       */
+                                       _have_old = false;
+                               }
+                       }
+
+                       _current  = v;
+               } 
        }
 
        virtual std::string to_string (T const& v) const             = 0;
@@ -171,9 +189,15 @@ protected:
        bool _have_old;
        T _current;
        T _old;
+
+private:
+       /* disallow copy-construction; it's not obvious whether it should mean
+          a copy of just the value, or the value and property ID.
+       */
+       PropertyTemplate (PropertyTemplate<T> const &);
 };
 
-template<class T>
+template<class T> /*LIBPBD_API*/ 
 std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
 {
        return os << s.val ();
@@ -183,7 +207,7 @@ std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s)
  *  with types that can be written to / read from stringstreams.
  */
 template<class T>
-class Property : public PropertyTemplate<T>
+class /*LIBPBD_API*/ Property : public PropertyTemplate<T>
 {
 public:
        Property (PropertyDescriptor<T> q, T const& v)
@@ -194,17 +218,15 @@ public:
                : PropertyTemplate<T> (q, o, c)
        {}
 
+       Property (PropertyDescriptor<T> q, Property<T> const& v)
+               : PropertyTemplate<T> (q, v)
+       {}
+
        Property<T>* clone () const {
-               return new Property<T> (*this);
+               return new Property<T> (this->property_id(), this->_old, this->_current);
        }
        
-        void get_changes_as_properties (PropertyList& changes, Command *) const {
-                if (this->_have_old) {
-                       changes.add (new Property<T> (*this));
-                }
-        }
-
-        Property<T>* maybe_clone_self_if_found_in_history_node (const XMLNode& node) const {
+       Property<T>* clone_from_xml (const XMLNode& node) const {
                XMLNodeList const & children = node.children ();
                XMLNodeList::const_iterator i = children.begin();
                while (i != children.end() && (*i)->name() != this->property_name()) {
@@ -222,7 +244,7 @@ public:
                }
                        
                return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ()));
-        }
+       }
 
        T & operator=(T const& v) {
                this->set (v);
@@ -232,9 +254,8 @@ public:
 private:
         friend class PropertyFactory;
 
-       Property (PropertyDescriptor<T> q)
-               : PropertyTemplate<T> (q)
-       {}
+       /* no copy-construction */
+       Property (Property<T> const &);
 
        /* Note that we do not set a locale for the streams used
         * in to_string() or from_string(), because we want the
@@ -264,41 +285,41 @@ private:
  *  separators, etc.
  */
 template<>
-class Property<std::string> : public PropertyTemplate<std::string>
+class /*LIBPBD_API*/ Property<std::string> : public PropertyTemplate<std::string>
 {
 public:
-       Property (PropertyDescriptor<std::string> q, std::string const& v)
-               : PropertyTemplate<std::string> (q, v)
+       Property (PropertyDescriptor<std::string> d, std::string const & v)
+               : PropertyTemplate<std::string> (d, v)
        {}
 
+       Property (PropertyDescriptor<std::string> d, std::string const & o, std::string const & c)
+               : PropertyTemplate<std::string> (d, o, c)
+       {}
+       
        Property<std::string>* clone () const {
-               return new Property<std::string> (*this);
+               return new Property<std::string> (this->property_id(), _old, _current);
        }
-       
-        void get_changes_as_properties (PropertyList& changes, Command* /*ignored*/) const {
-                if (this->_have_old) {
-                       changes.add (new Property<std::string> (*this));
-               }
-        }
 
-       std::string & operator=(std::string const& v) {
+       std::string & operator= (std::string const& v) {
                this->set (v);
                return this->_current;
        }
 
 private:
        std::string to_string (std::string const& v) const {
-               return _current;
+               return v;
        }
 
        std::string from_string (std::string const& s) const {
                return s;
        }
 
+       /* no copy-construction */
+       Property (Property<std::string> const &);
 };
 
 template<class T>
-class EnumProperty : public Property<T>
+class /*LIBPBD_API*/ EnumProperty : public Property<T>
 {
 public:
        EnumProperty (PropertyDescriptor<T> q, T const& v)
@@ -318,8 +339,125 @@ private:
        T from_string (std::string const & s) const {
                return static_cast<T> (string_2_enum (s, this->_current));
        }
+
+       /* no copy-construction */
+       EnumProperty (EnumProperty const &);
 };
+
+/** A Property which holds a shared_ptr to a Stateful object,
+ *  and handles undo using the somewhat inefficient approach
+ *  of saving the complete XML state of its object before and
+ *  after changes.  A sort of half-way house between the old
+ *  complete-state undo system and the new difference-based
+ *  one.
+ */
+template <class T>
+class /*LIBPBD_API*/ SharedStatefulProperty : public PropertyBase
+{
+public:
+       typedef boost::shared_ptr<T> Ptr;
        
+       SharedStatefulProperty (PropertyID d, Ptr p)
+               : PropertyBase (d)
+               , _current (p)
+       {
+               
+       }
+
+       SharedStatefulProperty (PropertyID d, Ptr o, Ptr c)
+               : PropertyBase (d)
+               , _old (o)
+               , _current (c)
+       {
+               
+       }
+
+       bool set_value (XMLNode const & node) {
+
+               /* Look for our node */
+               XMLNode* n = node.child (property_name ());
+               if (!n) {
+                       return false;
+               }
+
+               /* And there should be one child which is the state of our T */
+               XMLNodeList const & children = n->children ();
+               if (children.size() != 1) {
+                       return false;
+               }
+
+               _current->set_state (*children.front (), Stateful::current_state_version);
+               return true;
+       }
+
+       void get_value (XMLNode & node) const {
+               XMLNode* n = node.add_child (property_name ());
+               n->add_child_nocopy (_current->get_state ());
+       }
+
+       void clear_changes () {
+               /* We are starting to change things, so _old gets set up
+                  with the current state.
+               */
+               _old.reset (new T (*_current.get()));
+       }
+
+       bool changed () const {
+               /* Expensive, but, hey; this requires operator!= in
+                  our T
+               */
+               return (*_old != *_current);
+       }
+
+       void invert () {
+               _current.swap (_old);
+       }
+
+       void get_changes_as_xml (XMLNode* history_node) const {
+               /* We express the diff as before and after state, just
+                  as MementoCommand does.
+               */
+               XMLNode* p = history_node->add_child (property_name ());
+               XMLNode* from = p->add_child ("from");
+               from->add_child_nocopy (_old->get_state ());
+               XMLNode* to = p->add_child ("to");
+               to->add_child_nocopy (_current->get_state ());
+       }
+
+       void get_changes_as_properties (PropertyList& changes, Command *) const {
+               if (changed ()) {
+                       changes.add (clone ());
+               }
+       }
+
+       void apply_changes (PropertyBase const * p) {
+               *_current = *(dynamic_cast<SharedStatefulProperty const *> (p))->val ();
+       }
+
+       Ptr val () const {
+               return _current;
+       }
+
+       T* operator-> () const {
+               return _current.operator-> ();
+       }
+
+       operator bool () const {
+               return _current;
+       }
+
+protected:
+
+       Ptr _old;
+       Ptr _current;
+
+private:
+
+       /* No copy-construction nor assignment */
+       SharedStatefulProperty (SharedStatefulProperty<T> const &);
+       SharedStatefulProperty<T>& operator= (SharedStatefulProperty<T> const &);
+};
+
 } /* namespace PBD */
 
 #include "pbd/property_list_impl.h"