summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/editor_drag.cc9
-rw-r--r--libs/ardour/ardour/region.h7
-rw-r--r--libs/ardour/region.cc100
-rw-r--r--libs/pbd/pbd/stateful.h116
-rw-r--r--libs/pbd/stateful.cc31
-rw-r--r--libs/pbd/wscript1
6 files changed, 208 insertions, 56 deletions
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index 17c09737d2..2dec55a0ad 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -21,6 +21,7 @@
#include <stdint.h>
#include "pbd/memento_command.h"
#include "pbd/basename.h"
+#include "pbd/stateful_diff_command.h"
#include "ardour/diskstream.h"
#include "ardour/region_command.h"
#include "ardour/session.h"
@@ -950,7 +951,7 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
}
} else {
- RegionCommand* rcmd = new RegionCommand (rv->region());
+ rv->region()->clear_history ();
/*
motion on the same track. plonk the previously reparented region
@@ -967,10 +968,8 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
if (dest_rtv->view()->layer_display() == Stacked) {
- layer_t old_layer = rv->region()->layer();
rv->region()->set_layer (dest_layer);
rv->region()->set_pending_explicit_relayer (true);
- rcmd->add_property_change (RegionCommand::Layer, old_layer, dest_layer);
}
/* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
@@ -981,11 +980,9 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
playlist->freeze();
}
- nframes64_t old_pos = rv->region()->position();
rv->region()->set_position (where, (void*) this);
- rcmd->add_property_change (RegionCommand::Position, old_pos, where);
- _editor->session()->add_command (rcmd);
+ _editor->session()->add_command (new StatefulDiffCommand (rv->region().get()));
}
if (changed_tracks && !_copy) {
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h
index 310bd582cd..7060607a8d 100644
--- a/libs/ardour/ardour/region.h
+++ b/libs/ardour/ardour/region.h
@@ -315,14 +315,14 @@ class Region
DataType _type;
Flag _flags;
- nframes_t _start;
+ PBD::State<nframes_t> _start;
nframes_t _length;
nframes_t _last_length;
- nframes_t _position;
+ PBD::State<nframes_t> _position;
nframes_t _last_position;
PositionLockStyle _positional_lock_style;
nframes_t _sync_position;
- layer_t _layer;
+ PBD::State<layer_t> _layer;
mutable RegionEditState _first_edit;
int _frozen;
nframes64_t _ancestral_start;
@@ -347,6 +347,7 @@ class Region
private:
+ void register_states ();
void use_sources (SourceList const &);
};
diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc
index f1dd92abe5..e9b896f26e 100644
--- a/libs/ardour/region.cc
+++ b/libs/ardour/region.cc
@@ -56,18 +56,27 @@ Change Region::HiddenChanged = ARDOUR::new_change ();
PBD::Signal1<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
+void
+Region::register_states ()
+{
+ _xml_node_name = X_("Region");
+ add_state (_start);
+ add_state (_position);
+ add_state (_layer);
+}
+
/* derived-from-derived constructor (no sources in constructor) */
Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
: SessionObject(s, name)
, _type(type)
, _flags(Flag (flags|DoNotSendPropertyChanges))
- , _start(start)
+ , _start (X_("start"), start)
, _length(length)
- , _position(0)
+ , _position (X_("position"), 0)
, _last_position(0)
, _positional_lock_style(AudioTime)
, _sync_position(_start)
- , _layer(layer)
+ , _layer (X_("layer"), layer)
, _first_edit(EditChangesNothing)
, _frozen(0)
, _ancestral_start (0)
@@ -79,6 +88,8 @@ Region::Region (Session& s, nframes_t start, nframes_t length, const string& nam
, _last_layer_op(0)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
/* no sources at this point */
}
@@ -87,13 +98,13 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
: SessionObject(src->session(), name)
, _type(type)
, _flags(Flag (flags|DoNotSendPropertyChanges))
- , _start(start)
+ , _start (X_("start"), start)
, _length(length)
- , _position(0)
+ , _position (X_("position"), 0)
, _last_position(0)
, _positional_lock_style(AudioTime)
, _sync_position(_start)
- , _layer(layer)
+ , _layer (X_("layer"), layer)
, _first_edit(EditChangesNothing)
, _frozen(0)
, _ancestral_start (0)
@@ -106,6 +117,8 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
, _last_layer_op(0)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
_sources.push_back (src);
_master_sources.push_back (src);
@@ -120,13 +133,13 @@ Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const
: SessionObject(srcs.front()->session(), name)
, _type(type)
, _flags(Flag (flags|DoNotSendPropertyChanges))
- , _start(start)
+ , _start (X_("start"), start)
, _length(length)
- , _position(0)
+ , _position (X_("position"), 0)
, _last_position(0)
, _positional_lock_style(AudioTime)
, _sync_position(_start)
- , _layer(layer)
+ , _layer (X_("layer"), layer)
, _first_edit(EditChangesNothing)
, _frozen(0)
, _ancestral_start (0)
@@ -138,6 +151,8 @@ Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const
, _last_layer_op(0)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
use_sources (srcs);
assert(_sources.size() > 0);
}
@@ -146,9 +161,14 @@ Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const
Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
: SessionObject(other->session(), name)
, _type (other->data_type())
+ , _start (X_("start"), 0)
+ , _position (X_("position"), 0)
+ , _layer (X_("layer"), 0)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
_start = other->_start + offset;
copy_stuff (other, offset, length, name, layer, flags);
@@ -186,8 +206,13 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
: SessionObject(other->session(), name)
, _type (other->data_type())
+ , _start (X_("start"), 0)
+ , _position (X_("position"), 0)
+ , _layer (X_("layer"), 0)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
/* create a new Region exactly like another but starting at 0 in its sources */
_start = 0;
@@ -261,6 +286,8 @@ Region::Region (boost::shared_ptr<const Region> other)
, _last_layer_op(other->_last_layer_op)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
_flags = Flag (_flags | DoNotSendPropertyChanges);
other->_first_edit = EditChangesName;
@@ -279,13 +306,13 @@ Region::Region (const SourceList& srcs, const XMLNode& node)
: SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
, _type(DataType::NIL) // to be loaded from XML
, _flags(DoNotSendPropertyChanges)
- , _start(0)
+ , _start (X_("start"), 0)
, _length(0)
- , _position(0)
+ , _position (X_("position"), 0)
, _last_position(0)
, _positional_lock_style(AudioTime)
, _sync_position(_start)
- , _layer(0)
+ , _layer (X_("layer"), 0)
, _first_edit(EditChangesNothing)
, _frozen(0)
, _stretch(1.0)
@@ -295,6 +322,8 @@ Region::Region (const SourceList& srcs, const XMLNode& node)
, _last_layer_op(0)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
use_sources (srcs);
if (set_state (node, Stateful::loading_state_version)) {
@@ -309,13 +338,13 @@ Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
: SessionObject(src->session(), X_("error: XML did not reset this"))
, _type(DataType::NIL)
, _flags(DoNotSendPropertyChanges)
- , _start(0)
+ , _start (X_("start"), 0)
, _length(0)
- , _position(0)
+ , _position (X_("position"), 0)
, _last_position(0)
, _positional_lock_style(AudioTime)
, _sync_position(_start)
- , _layer(0)
+ , _layer (X_("layer"), 0)
, _first_edit(EditChangesNothing)
, _frozen(0)
, _stretch(1.0)
@@ -325,6 +354,8 @@ Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
, _last_layer_op(0)
, _pending_explicit_relayer (false)
{
+ register_states ();
+
_sources.push_back (src);
if (set_state (node, Stateful::loading_state_version)) {
@@ -1065,11 +1096,11 @@ Region::state (bool /*full_state*/)
node->add_property ("id", buf);
node->add_property ("name", _name);
node->add_property ("type", _type.to_string());
- snprintf (buf, sizeof (buf), "%u", _start);
+ snprintf (buf, sizeof (buf), "%u", _start.get ());
node->add_property ("start", buf);
snprintf (buf, sizeof (buf), "%u", _length);
node->add_property ("length", buf);
- snprintf (buf, sizeof (buf), "%u", _position);
+ snprintf (buf, sizeof (buf), "%u", _position.get ());
node->add_property ("position", buf);
snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
node->add_property ("ancestral-start", buf);
@@ -1099,7 +1130,7 @@ Region::state (bool /*full_state*/)
/* note: flags are stored by derived classes */
- snprintf (buf, sizeof (buf), "%d", (int) _layer);
+ snprintf (buf, sizeof (buf), "%d", (int) _layer.get());
node->add_property ("layer", buf);
snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
node->add_property ("sync-position", buf);
@@ -1131,13 +1162,10 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
that are mutable after construction.
*/
- if ((prop = node.property ("name")) == 0) {
- error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
- return -1;
+ if ((prop = node.property ("name"))) {
+ _name = prop->value();
}
- _name = prop->value();
-
if ((prop = node.property ("type")) == 0) {
_type = DataType::AUDIO;
} else {
@@ -1151,8 +1179,6 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
cerr << _name << " start changed\n";
_start = val;
}
- } else {
- _start = 0;
}
if ((prop = node.property ("length")) != 0) {
@@ -1163,9 +1189,6 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
_last_length = _length;
_length = val;
}
- } else {
- _last_length = _length;
- _length = 1;
}
if ((prop = node.property ("position")) != 0) {
@@ -1176,9 +1199,6 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
_last_position = _position;
_position = val;
}
- } else {
- _last_position = _position;
- _position = 0;
}
if ((prop = node.property ("layer")) != 0) {
@@ -1189,8 +1209,6 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
cerr << _name << " layer changed\n";
_layer = x;
}
- } else {
- _layer = 0;
}
if ((prop = node.property ("sync-position")) != 0) {
@@ -1200,8 +1218,6 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
cerr << _name << " sync changed\n";
_sync_position = val;
}
- } else {
- _sync_position = _start;
}
if ((prop = node.property ("positional-lock-style")) != 0) {
@@ -1221,8 +1237,6 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
}
}
- } else {
- _positional_lock_style = AudioTime;
}
/* XXX FIRST EDIT !!! */
@@ -1270,9 +1284,6 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
/* note: derived classes set flags */
- delete _extra_xml;
- _extra_xml = 0;
-
for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLNode *child;
@@ -1280,6 +1291,7 @@ Region::set_live_state (const XMLNode& node, int /*version*/, Change& what_chang
child = (*niter);
if (child->name () == "Extra") {
+ delete _extra_xml;
_extra_xml = new XMLNode (*child);
break;
}
@@ -1301,13 +1313,10 @@ Region::set_state (const XMLNode& node, int version)
/* ID is not allowed to change, ever */
- if ((prop = node.property ("id")) == 0) {
- error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
- return -1;
+ if ((prop = node.property ("id"))) {
+ _id = prop->value();
}
- _id = prop->value();
-
_first_edit = EditChangesNothing;
set_live_state (node, version, what_changed, true);
@@ -1382,6 +1391,7 @@ Region::send_change (Change what_changed)
cerr << _name << " actually sends prop change " << hex << what_changed << dec << " @ " << get_microseconds() << endl;
RegionPropertyChanged (rptr);
cerr << _name << " done with prop change @ " << get_microseconds() << endl;
+
} catch (...) {
/* no shared_ptr available, relax; */
}
diff --git a/libs/pbd/pbd/stateful.h b/libs/pbd/pbd/stateful.h
index 0204c8084a..60c1c6b23c 100644
--- a/libs/pbd/pbd/stateful.h
+++ b/libs/pbd/pbd/stateful.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2000 Paul Davis
+ Copyright (C) 2000-2010 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,9 @@
#define __pbd_stateful_h__
#include <string>
+#include <cassert>
#include "pbd/id.h"
+#include "pbd/xml++.h"
class XMLNode;
@@ -31,15 +33,119 @@ namespace sys {
class path;
}
+/** Base (non template) part of State */
+class StateBase
+{
+public:
+ StateBase (std::string const & p)
+ : _have_old (false)
+ , _xml_property_name (p)
+ {
+
+ }
+
+ StateBase (StateBase const & s)
+ : _have_old (s._have_old)
+ , _xml_property_name (s._xml_property_name)
+ {
+
+ }
+
+ /** Forget about any old value for this state */
+ void clear_history () {
+ _have_old = false;
+ }
+
+ virtual void diff (XMLNode *, XMLNode *) const = 0;
+
+protected:
+ bool _have_old;
+ std::string _xml_property_name;
+};
+
+/** Class to represent a single piece of state in a Stateful object */
+template <class T>
+class State : public StateBase
+{
+public:
+ State (std::string const & p, T const & v)
+ : StateBase (p)
+ , _current (v)
+ {
+
+ }
+
+ State (State<T> const & s)
+ : StateBase (s)
+ {
+ _current = s._current;
+ _old = s._old;
+ }
+
+ State<T> & operator= (State<T> const & s) {
+ /* XXX: isn't there a nicer place to do this? */
+ _have_old = s._have_old;
+ _xml_property_name = s._xml_property_name;
+
+ _current = s._current;
+ _old = s._old;
+ return *this;
+ }
+
+ T & operator= (T const & v) {
+ set (v);
+ return _current;
+ }
+
+ T & operator+= (T const & v) {
+ set (_current + v);
+ return _current;
+ }
+
+ operator T () const {
+ return _current;
+ }
+
+ T const & get () const {
+ return _current;
+ }
+
+ void diff (XMLNode* old, XMLNode* current) const {
+ if (_have_old) {
+ std::stringstream o;
+ o << _old;
+ old->add_property (_xml_property_name.c_str(), o.str().c_str());
+ std::stringstream c;
+ c << _current;
+ current->add_property (_xml_property_name.c_str(), c.str().c_str());
+ }
+ }
+
+private:
+ void set (T const & v) {
+ _old = _current;
+ _have_old = true;
+ _current = v;
+ }
+
+ T _current;
+ T _old;
+};
+
+/** Base class for objects with saveable and undoable state */
class Stateful {
public:
- Stateful();
+ Stateful ();
virtual ~Stateful();
virtual XMLNode& get_state (void) = 0;
virtual int set_state (const XMLNode&, int version) = 0;
+ void add_state (StateBase & s) {
+ _states.push_back (&s);
+ }
+
/* Extra XML nodes */
void add_extra_xml (XMLNode&);
@@ -47,6 +153,9 @@ class Stateful {
const PBD::ID& id() const { return _id; }
+ void clear_history ();
+ std::pair<XMLNode *, XMLNode*> diff ();
+
static int current_state_version;
static int loading_state_version;
@@ -58,6 +167,9 @@ class Stateful {
XMLNode *_extra_xml;
XMLNode *_instant_xml;
PBD::ID _id;
+
+ std::string _xml_node_name; ///< name of node to use for this object in XML
+ std::list<StateBase*> _states; ///< state variables that this object has
};
} // namespace PBD
diff --git a/libs/pbd/stateful.cc b/libs/pbd/stateful.cc
index 596402576e..9f510b85d4 100644
--- a/libs/pbd/stateful.cc
+++ b/libs/pbd/stateful.cc
@@ -47,6 +47,10 @@ Stateful::~Stateful ()
// means it needs to live on indefinately.
delete _instant_xml;
+
+ for (list<StateBase*>::iterator i = _states.begin(); i != _states.end(); ++i) {
+ delete *i;
+ }
}
void
@@ -149,4 +153,31 @@ Stateful::instant_xml (const string& str, const sys::path& directory_path)
return 0;
}
+/** Forget about any old state for this object */
+void
+Stateful::clear_history ()
+{
+ for (list<StateBase*>::iterator i = _states.begin(); i != _states.end(); ++i) {
+ (*i)->clear_history ();
+ }
+}
+
+/** @return A pair of XMLNodes representing state that has changed since the last time clear_history
+ * was called on this object; the first is the state before, the second the state after.
+ *
+ * It is the caller's responsibility to delete the returned XMLNodes.
+ */
+pair<XMLNode *, XMLNode *>
+Stateful::diff ()
+{
+ XMLNode* old = new XMLNode (_xml_node_name);
+ XMLNode* current = new XMLNode (_xml_node_name);
+
+ for (list<StateBase*>::iterator i = _states.begin(); i != _states.end(); ++i) {
+ (*i)->diff (old, current);
+ }
+
+ return make_pair (old, current);
+}
+
} // namespace PBD
diff --git a/libs/pbd/wscript b/libs/pbd/wscript
index 1f458678bd..8bd00bcff9 100644
--- a/libs/pbd/wscript
+++ b/libs/pbd/wscript
@@ -80,6 +80,7 @@ def build(bld):
shortpath.cc
signals.cc
stacktrace.cc
+ stateful_diff_command.cc
stateful.cc
strreplace.cc
strsplit.cc