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:
parent
5ce47b52da
commit
66062a85b6
|
@ -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));
|
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()->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);
|
cp.move_to (x, y, ControlPoint::Full);
|
||||||
reset_line_coords (cp);
|
reset_line_coords (cp);
|
||||||
|
@ -233,7 +235,10 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y)
|
||||||
|
|
||||||
update_pending = false;
|
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()->commit_reversible_command ();
|
||||||
trackview.editor().session()->set_dirty ();
|
trackview.editor().session()->set_dirty ();
|
||||||
}
|
}
|
||||||
|
@ -568,7 +573,9 @@ void
|
||||||
AutomationLine::start_drag_single (ControlPoint* cp, double x, float fraction)
|
AutomationLine::start_drag_single (ControlPoint* cp, double x, float fraction)
|
||||||
{
|
{
|
||||||
trackview.editor().session()->begin_reversible_command (_("automation event move"));
|
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.clear ();
|
||||||
_drag_points.push_back (cp);
|
_drag_points.push_back (cp);
|
||||||
|
@ -593,7 +600,9 @@ void
|
||||||
AutomationLine::start_drag_line (uint32_t i1, uint32_t i2, float fraction)
|
AutomationLine::start_drag_line (uint32_t i1, uint32_t i2, float fraction)
|
||||||
{
|
{
|
||||||
trackview.editor().session()->begin_reversible_command (_("automation range move"));
|
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 ();
|
_drag_points.clear ();
|
||||||
for (uint32_t i = i1; i <= i2; i++) {
|
for (uint32_t i = i1; i <= i2; i++) {
|
||||||
|
@ -611,7 +620,9 @@ void
|
||||||
AutomationLine::start_drag_multiple (list<ControlPoint*> cp, float fraction, XMLNode* state)
|
AutomationLine::start_drag_multiple (list<ControlPoint*> cp, float fraction, XMLNode* state)
|
||||||
{
|
{
|
||||||
trackview.editor().session()->begin_reversible_command (_("automation range move"));
|
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;
|
_drag_points = cp;
|
||||||
start_drag_common (0, fraction);
|
start_drag_common (0, fraction);
|
||||||
|
@ -778,7 +789,10 @@ AutomationLine::end_drag ()
|
||||||
|
|
||||||
update_pending = false;
|
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()->commit_reversible_command ();
|
||||||
trackview.editor().session()->set_dirty ();
|
trackview.editor().session()->set_dirty ();
|
||||||
}
|
}
|
||||||
|
@ -924,8 +938,10 @@ AutomationLine::remove_point (ControlPoint& cp)
|
||||||
|
|
||||||
alist->erase (mr.start, mr.end);
|
alist->erase (mr.start, mr.end);
|
||||||
|
|
||||||
trackview.editor().session()->add_command(new MementoCommand<AutomationList>(
|
trackview.editor().session()->add_command(
|
||||||
*alist.get(), &before, &alist->get_state()));
|
new MementoCommand<AutomationList> (memento_command_binder (), &before, &alist->get_state())
|
||||||
|
);
|
||||||
|
|
||||||
trackview.editor().session()->commit_reversible_command ();
|
trackview.editor().session()->commit_reversible_command ();
|
||||||
trackview.editor().session()->set_dirty ();
|
trackview.editor().session()->set_dirty ();
|
||||||
}
|
}
|
||||||
|
@ -1107,7 +1123,11 @@ AutomationLine::clear ()
|
||||||
/* parent must create command */
|
/* parent must create command */
|
||||||
XMLNode &before = alist->get_state();
|
XMLNode &before = alist->get_state();
|
||||||
alist->clear();
|
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()->commit_reversible_command ();
|
||||||
trackview.editor().session()->set_dirty ();
|
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()
|
_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());
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
#include "pbd/undo.h"
|
#include "pbd/undo.h"
|
||||||
#include "pbd/statefuldestructible.h"
|
#include "pbd/statefuldestructible.h"
|
||||||
|
#include "pbd/memento_command.h"
|
||||||
|
|
||||||
#include "ardour/automation_list.h"
|
#include "ardour/automation_list.h"
|
||||||
#include "ardour/types.h"
|
#include "ardour/types.h"
|
||||||
|
@ -131,6 +132,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
|
||||||
void add_always_in_view (double);
|
void add_always_in_view (double);
|
||||||
void clear_always_in_view ();
|
void clear_always_in_view ();
|
||||||
|
|
||||||
|
virtual MementoCommandBinder<ARDOUR::AutomationList>* memento_command_binder ();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
|
|
@ -22,10 +22,13 @@
|
||||||
#include "ardour/event_type_map.h"
|
#include "ardour/event_type_map.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
#include "ardour/source.h"
|
#include "ardour/source.h"
|
||||||
|
#include "ardour/midi_automation_list_binder.h"
|
||||||
|
#include "ardour/midi_region.h"
|
||||||
|
|
||||||
#include "automation_region_view.h"
|
#include "automation_region_view.h"
|
||||||
#include "gui_thread.h"
|
#include "gui_thread.h"
|
||||||
#include "public_editor.h"
|
#include "public_editor.h"
|
||||||
|
#include "midi_automation_line.h"
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
|
@ -71,9 +74,12 @@ AutomationRegionView::init (Gdk::Color const & basic_color, bool /*wfd*/)
|
||||||
void
|
void
|
||||||
AutomationRegionView::create_line (boost::shared_ptr<ARDOUR::AutomationList> list)
|
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()),
|
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_colors();
|
||||||
_line->set_height ((uint32_t)rint(trackview.current_height() - NAME_HIGHLIGHT_SIZE));
|
_line->set_height ((uint32_t)rint(trackview.current_height() - NAME_HIGHLIGHT_SIZE));
|
||||||
_line->show();
|
_line->show();
|
||||||
|
@ -130,8 +136,15 @@ AutomationRegionView::add_automation_event (GdkEvent *, nframes_t when, double y
|
||||||
_line->the_list()->add (when_d, y);
|
_line->the_list()->add (when_d, y);
|
||||||
|
|
||||||
XMLNode& after = _line->the_list()->get_state();
|
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->session()->set_dirty ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,7 @@ gtk2_ardour_sources = [
|
||||||
'location_ui.cc',
|
'location_ui.cc',
|
||||||
'main.cc',
|
'main.cc',
|
||||||
'marker.cc',
|
'marker.cc',
|
||||||
|
'midi_automation_line.cc',
|
||||||
'midi_channel_selector.cc',
|
'midi_channel_selector.cc',
|
||||||
'midi_cut_buffer.cc',
|
'midi_cut_buffer.cc',
|
||||||
'midi_list_editor.cc',
|
'midi_list_editor.cc',
|
||||||
|
|
|
@ -1204,8 +1204,11 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
||||||
/* SOURCES */
|
/* SOURCES */
|
||||||
|
|
||||||
mutable Glib::Mutex source_lock;
|
mutable Glib::Mutex source_lock;
|
||||||
|
|
||||||
|
public:
|
||||||
typedef std::map<PBD::ID,boost::shared_ptr<Source> > SourceMap;
|
typedef std::map<PBD::ID,boost::shared_ptr<Source> > SourceMap;
|
||||||
|
|
||||||
|
private:
|
||||||
SourceMap sources;
|
SourceMap sources;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "ardour/midi_region.h"
|
#include "ardour/midi_region.h"
|
||||||
#include "ardour/session_playlists.h"
|
#include "ardour/session_playlists.h"
|
||||||
#include "ardour/region_factory.h"
|
#include "ardour/region_factory.h"
|
||||||
|
#include "ardour/midi_automation_list_binder.h"
|
||||||
#include "pbd/error.h"
|
#include "pbd/error.h"
|
||||||
#include "pbd/id.h"
|
#include "pbd/id.h"
|
||||||
#include "pbd/statefuldestructible.h"
|
#include "pbd/statefuldestructible.h"
|
||||||
|
@ -58,7 +59,12 @@ Session::memento_command_factory(XMLNode *n)
|
||||||
XMLNode *child = 0;
|
XMLNode *child = 0;
|
||||||
|
|
||||||
/* get id */
|
/* 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 */
|
/* get before/after */
|
||||||
|
|
||||||
|
@ -121,11 +127,20 @@ Session::memento_command_factory(XMLNode *n)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (obj_T == "Evoral::Curve" || obj_T == "ARDOUR::AutomationList") {
|
} else if (obj_T == "Evoral::Curve" || obj_T == "ARDOUR::AutomationList") {
|
||||||
std::map<PBD::ID, AutomationList*>::iterator i = automation_lists.find(id);
|
if (have_id) {
|
||||||
if (i != automation_lists.end()) {
|
std::map<PBD::ID, AutomationList*>::iterator i = automation_lists.find(id);
|
||||||
return new MementoCommand<AutomationList>(*i->second, before, after);
|
if (i != automation_lists.end()) {
|
||||||
}
|
return new MementoCommand<AutomationList>(*i->second, before, after);
|
||||||
cerr << "Alist not found\n";
|
}
|
||||||
|
} 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
|
} else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits herea
|
||||||
return new MementoCommand<PBD::StatefulDestructible>(*registry[id], before, after);
|
return new MementoCommand<PBD::StatefulDestructible>(*registry[id], before, after);
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ libardour_sources = [
|
||||||
'location.cc',
|
'location.cc',
|
||||||
'location_importer.cc',
|
'location_importer.cc',
|
||||||
'meter.cc',
|
'meter.cc',
|
||||||
|
'midi_automation_list_binder.cc',
|
||||||
'midi_buffer.cc',
|
'midi_buffer.cc',
|
||||||
'midi_clock_slave.cc',
|
'midi_clock_slave.cc',
|
||||||
'midi_diskstream.cc',
|
'midi_diskstream.cc',
|
||||||
|
|
|
@ -31,6 +31,56 @@
|
||||||
#include <sigc++/slot.h>
|
#include <sigc++/slot.h>
|
||||||
#include <typeinfo>
|
#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
|
/** This command class is initialized with before and after mementos
|
||||||
* (from Stateful::get_state()), so undo becomes restoring the before
|
* (from Stateful::get_state()), so undo becomes restoring the before
|
||||||
* memento, and redo is restoring the after memento.
|
* memento, and redo is restoring the after memento.
|
||||||
|
@ -39,32 +89,38 @@ template <class obj_T>
|
||||||
class MementoCommand : public Command
|
class MementoCommand : public Command
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MementoCommand(obj_T& a_object, XMLNode* a_before, XMLNode* a_after)
|
MementoCommand (obj_T& a_object, XMLNode* a_before, XMLNode* a_after)
|
||||||
: obj(a_object), before(a_before), after(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 */
|
_binder->Destroyed.connect_same_thread (_binder_death_connection, boost::bind (&MementoCommand::binder_died, this));
|
||||||
obj.Destroyed.connect_same_thread (obj_death_connection, boost::bind (&MementoCommand::object_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 () {
|
~MementoCommand () {
|
||||||
drop_references ();
|
drop_references ();
|
||||||
delete before;
|
delete before;
|
||||||
delete after;
|
delete after;
|
||||||
|
delete _binder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void object_died () {
|
void binder_died () {
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator() () {
|
void operator() () {
|
||||||
if (after) {
|
if (after) {
|
||||||
obj.set_state(*after, Stateful::current_state_version);
|
_binder->get()->set_state(*after, Stateful::current_state_version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void undo() {
|
void undo() {
|
||||||
if (before) {
|
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);
|
XMLNode* node = new XMLNode(name);
|
||||||
|
_binder->add_state (node);
|
||||||
node->add_property("obj_id", obj.id().to_s());
|
|
||||||
node->add_property("type_name", demangled_name (obj));
|
node->add_property("type_name", demangled_name (*_binder->get()));
|
||||||
|
|
||||||
if (before) {
|
if (before) {
|
||||||
node->add_child_copy(*before);
|
node->add_child_copy(*before);
|
||||||
|
@ -95,10 +151,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
obj_T& obj;
|
MementoCommandBinder<obj_T>* _binder;
|
||||||
XMLNode* before;
|
XMLNode* before;
|
||||||
XMLNode* after;
|
XMLNode* after;
|
||||||
PBD::ScopedConnection obj_death_connection;
|
PBD::ScopedConnection _binder_death_connection;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __lib_pbd_memento_h__
|
#endif // __lib_pbd_memento_h__
|
||||||
|
|
Loading…
Reference in New Issue
Block a user