Fix binding of automation list undo records to MIDI sources. Should fix the remainder of #3203.

git-svn-id: svn://localhost/ardour2/branches/3.0@7411 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2010-07-14 12:27:37 +00:00
parent 5ce47b52da
commit 66062a85b6
8 changed files with 149 additions and 31 deletions

View File

@ -218,7 +218,9 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y)
double const x = trackview.editor().frame_to_unit (_time_converter.to((*cp.model())->when));
trackview.editor().session()->begin_reversible_command (_("automation event move"));
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
trackview.editor().session()->add_command (
new MementoCommand<AutomationList> (memento_command_binder(), &get_state(), 0)
);
cp.move_to (x, y, ControlPoint::Full);
reset_line_coords (cp);
@ -233,7 +235,10 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y)
update_pending = false;
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
trackview.editor().session()->add_command (
new MementoCommand<AutomationList> (memento_command_binder(), 0, &alist->get_state())
);
trackview.editor().session()->commit_reversible_command ();
trackview.editor().session()->set_dirty ();
}
@ -568,7 +573,9 @@ void
AutomationLine::start_drag_single (ControlPoint* cp, double x, float fraction)
{
trackview.editor().session()->begin_reversible_command (_("automation event move"));
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
trackview.editor().session()->add_command (
new MementoCommand<AutomationList> (memento_command_binder(), &get_state(), 0)
);
_drag_points.clear ();
_drag_points.push_back (cp);
@ -593,7 +600,9 @@ void
AutomationLine::start_drag_line (uint32_t i1, uint32_t i2, float fraction)
{
trackview.editor().session()->begin_reversible_command (_("automation range move"));
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
trackview.editor().session()->add_command (
new MementoCommand<AutomationList> (memento_command_binder (), &get_state(), 0)
);
_drag_points.clear ();
for (uint32_t i = i1; i <= i2; i++) {
@ -611,7 +620,9 @@ void
AutomationLine::start_drag_multiple (list<ControlPoint*> cp, float fraction, XMLNode* state)
{
trackview.editor().session()->begin_reversible_command (_("automation range move"));
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), state, 0));
trackview.editor().session()->add_command (
new MementoCommand<AutomationList> (memento_command_binder(), state, 0)
);
_drag_points = cp;
start_drag_common (0, fraction);
@ -778,7 +789,10 @@ AutomationLine::end_drag ()
update_pending = false;
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
trackview.editor().session()->add_command (
new MementoCommand<AutomationList>(memento_command_binder (), 0, &alist->get_state())
);
trackview.editor().session()->commit_reversible_command ();
trackview.editor().session()->set_dirty ();
}
@ -924,8 +938,10 @@ AutomationLine::remove_point (ControlPoint& cp)
alist->erase (mr.start, mr.end);
trackview.editor().session()->add_command(new MementoCommand<AutomationList>(
*alist.get(), &before, &alist->get_state()));
trackview.editor().session()->add_command(
new MementoCommand<AutomationList> (memento_command_binder (), &before, &alist->get_state())
);
trackview.editor().session()->commit_reversible_command ();
trackview.editor().session()->set_dirty ();
}
@ -1107,7 +1123,11 @@ AutomationLine::clear ()
/* parent must create command */
XMLNode &before = alist->get_state();
alist->clear();
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*(alist.get()), &before, &alist->get_state()));
trackview.editor().session()->add_command (
new MementoCommand<AutomationList> (memento_command_binder (), &before, &alist->get_state())
);
trackview.editor().session()->commit_reversible_command ();
trackview.editor().session()->set_dirty ();
}
@ -1311,3 +1331,9 @@ AutomationLine::connect_to_list ()
_list_connections, invalidator (*this), boost::bind (&AutomationLine::interpolation_changed, this, _1), gui_context()
);
}
MementoCommandBinder<AutomationList>*
AutomationLine::memento_command_binder ()
{
return new SimpleMementoCommandBinder<AutomationList> (*alist.get());
}

View File

@ -34,6 +34,7 @@
#include "pbd/undo.h"
#include "pbd/statefuldestructible.h"
#include "pbd/memento_command.h"
#include "ardour/automation_list.h"
#include "ardour/types.h"
@ -131,6 +132,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
void add_always_in_view (double);
void clear_always_in_view ();
virtual MementoCommandBinder<ARDOUR::AutomationList>* memento_command_binder ();
protected:
std::string _name;

View File

@ -22,10 +22,13 @@
#include "ardour/event_type_map.h"
#include "ardour/session.h"
#include "ardour/source.h"
#include "ardour/midi_automation_list_binder.h"
#include "ardour/midi_region.h"
#include "automation_region_view.h"
#include "gui_thread.h"
#include "public_editor.h"
#include "midi_automation_line.h"
#include "i18n.h"
@ -71,9 +74,12 @@ AutomationRegionView::init (Gdk::Color const & basic_color, bool /*wfd*/)
void
AutomationRegionView::create_line (boost::shared_ptr<ARDOUR::AutomationList> list)
{
_line = boost::shared_ptr<AutomationLine>(new AutomationLine(
_line = boost::shared_ptr<AutomationLine> (new MidiAutomationLine(
ARDOUR::EventTypeMap::instance().to_symbol(list->parameter()),
trackview, *get_canvas_group(), list, &_time_converter));
trackview, *get_canvas_group(), list,
boost::dynamic_pointer_cast<ARDOUR::MidiRegion> (_region),
_parameter,
&_time_converter));
_line->set_colors();
_line->set_height ((uint32_t)rint(trackview.current_height() - NAME_HIGHLIGHT_SIZE));
_line->show();
@ -130,8 +136,15 @@ AutomationRegionView::add_automation_event (GdkEvent *, nframes_t when, double y
_line->the_list()->add (when_d, y);
XMLNode& after = _line->the_list()->get_state();
view->session()->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(
*_line->the_list(), &before, &after));
/* XXX: hack! */
boost::shared_ptr<ARDOUR::MidiRegion> mr = boost::dynamic_pointer_cast<ARDOUR::MidiRegion> (_region);
assert (mr);
view->session()->commit_reversible_command (
new MementoCommand<ARDOUR::AutomationList> (new ARDOUR::MidiAutomationListBinder (mr->midi_source(), _parameter), &before, &after)
);
view->session()->set_dirty ();
}

View File

@ -127,6 +127,7 @@ gtk2_ardour_sources = [
'location_ui.cc',
'main.cc',
'marker.cc',
'midi_automation_line.cc',
'midi_channel_selector.cc',
'midi_cut_buffer.cc',
'midi_list_editor.cc',

View File

@ -1204,8 +1204,11 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
/* SOURCES */
mutable Glib::Mutex source_lock;
public:
typedef std::map<PBD::ID,boost::shared_ptr<Source> > SourceMap;
private:
SourceMap sources;
public:

View File

@ -33,6 +33,7 @@
#include "ardour/midi_region.h"
#include "ardour/session_playlists.h"
#include "ardour/region_factory.h"
#include "ardour/midi_automation_list_binder.h"
#include "pbd/error.h"
#include "pbd/id.h"
#include "pbd/statefuldestructible.h"
@ -58,7 +59,12 @@ Session::memento_command_factory(XMLNode *n)
XMLNode *child = 0;
/* get id */
id = PBD::ID(n->property("obj-id")->value());
/* XXX: HACK! */
bool have_id = n->property("obj-id") != 0;
if (have_id) {
id = PBD::ID(n->property("obj-id")->value());
}
/* get before/after */
@ -121,11 +127,20 @@ Session::memento_command_factory(XMLNode *n)
}
} else if (obj_T == "Evoral::Curve" || obj_T == "ARDOUR::AutomationList") {
std::map<PBD::ID, AutomationList*>::iterator i = automation_lists.find(id);
if (i != automation_lists.end()) {
return new MementoCommand<AutomationList>(*i->second, before, after);
}
cerr << "Alist not found\n";
if (have_id) {
std::map<PBD::ID, AutomationList*>::iterator i = automation_lists.find(id);
if (i != automation_lists.end()) {
return new MementoCommand<AutomationList>(*i->second, before, after);
}
} else {
return new MementoCommand<AutomationList> (
new MidiAutomationListBinder (n, sources),
before, after
);
}
cerr << "Alist not found\n";
} else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits herea
return new MementoCommand<PBD::StatefulDestructible>(*registry[id], before, after);
}

View File

@ -112,6 +112,7 @@ libardour_sources = [
'location.cc',
'location_importer.cc',
'meter.cc',
'midi_automation_list_binder.cc',
'midi_buffer.cc',
'midi_clock_slave.cc',
'midi_diskstream.cc',

View File

@ -31,6 +31,56 @@
#include <sigc++/slot.h>
#include <typeinfo>
/** A class that can return a Stateful object which is the subject of a MementoCommand.
*
* The existence of this class means that the undo record can refer to objects which
* don't exist in the session file. Currently this is just used for MIDI automation;
* when MIDI automation is edited, undo records are written for the AutomationList being
* changed. However this AutomationList is a temporary structure, built by a MidiModel,
* which doesn't get written to the session file. Hence we need to be able to go from
* a MidiSource and Parameter to an AutomationList. This Binder mechanism allows this
* through MidiAutomationListBinder; the undo record stores the source and parameter,
* and these are bound to an AutomationList by the Binder.
*/
template <class obj_T>
class MementoCommandBinder : public PBD::Destructible
{
public:
/** @return Stateful object to operate on */
virtual obj_T* get () = 0;
/** Add our own state to an XMLNode */
virtual void add_state (XMLNode *) = 0;
};
/** A simple MementoCommandBinder which binds directly to an object */
template <class obj_T>
class SimpleMementoCommandBinder : public MementoCommandBinder<obj_T>
{
public:
SimpleMementoCommandBinder (obj_T& o)
: _object (o)
{
_object.Destroyed.connect_same_thread (_object_death_connection, boost::bind (&SimpleMementoCommandBinder::object_died, this));
}
obj_T* get () {
return &_object;
}
void add_state (XMLNode* node) {
node->add_property ("obj_id", _object.id().to_s());
}
void object_died () {
this->drop_references ();
}
private:
obj_T& _object;
PBD::ScopedConnection _object_death_connection;
};
/** This command class is initialized with before and after mementos
* (from Stateful::get_state()), so undo becomes restoring the before
* memento, and redo is restoring the after memento.
@ -39,32 +89,38 @@ template <class obj_T>
class MementoCommand : public Command
{
public:
MementoCommand(obj_T& a_object, XMLNode* a_before, XMLNode* a_after)
: obj(a_object), before(a_before), after(a_after)
MementoCommand (obj_T& a_object, XMLNode* a_before, XMLNode* a_after)
: _binder (new SimpleMementoCommandBinder<obj_T> (a_object)), before (a_before), after (a_after)
{
/* if the object dies, make sure that we die and that everyone knows about it */
obj.Destroyed.connect_same_thread (obj_death_connection, boost::bind (&MementoCommand::object_died, this));
_binder->Destroyed.connect_same_thread (_binder_death_connection, boost::bind (&MementoCommand::binder_died, this));
}
MementoCommand (MementoCommandBinder<obj_T>* b, XMLNode* a_before, XMLNode* a_after)
: _binder (b), before (a_before), after (a_after)
{
_binder->Destroyed.connect_same_thread (_binder_death_connection, boost::bind (&MementoCommand::binder_died, this));
}
~MementoCommand () {
drop_references ();
delete before;
delete after;
delete _binder;
}
void object_died () {
void binder_died () {
delete this;
}
void operator() () {
if (after) {
obj.set_state(*after, Stateful::current_state_version);
_binder->get()->set_state(*after, Stateful::current_state_version);
}
}
void undo() {
if (before) {
obj.set_state(*before, Stateful::current_state_version);
_binder->get()->set_state(*before, Stateful::current_state_version);
}
}
@ -79,9 +135,9 @@ public:
}
XMLNode* node = new XMLNode(name);
node->add_property("obj_id", obj.id().to_s());
node->add_property("type_name", demangled_name (obj));
_binder->add_state (node);
node->add_property("type_name", demangled_name (*_binder->get()));
if (before) {
node->add_child_copy(*before);
@ -95,10 +151,10 @@ public:
}
protected:
obj_T& obj;
MementoCommandBinder<obj_T>* _binder;
XMLNode* before;
XMLNode* after;
PBD::ScopedConnection obj_death_connection;
PBD::ScopedConnection _binder_death_connection;
};
#endif // __lib_pbd_memento_h__