X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fpbd%2Fmemento_command.h;h=45cb100a36e6d967e28771b6f935b565ad1d465e;hb=08371ae2cf6231bbb8522645d14add41ffa9c1c0;hp=46c724e9ea61be335ac5a2790b84a11356783a0d;hpb=58631285534b35a325165427490a6ab9419f4c5d;p=ardour.git diff --git a/libs/pbd/pbd/memento_command.h b/libs/pbd/pbd/memento_command.h index 46c724e9ea..45cb100a36 100644 --- a/libs/pbd/pbd/memento_command.h +++ b/libs/pbd/pbd/memento_command.h @@ -1,5 +1,6 @@ /* - Copyright (C) 2006 Hans Fugal & Paul Davis + Copyright (C) 2006 Paul Davis + Author: Hans Fugal 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 @@ -15,85 +16,160 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: /local/undo/libs/pbd3/pbd/undo.h 132 2006-06-29T18:45:16.609763Z fugalh $ */ #ifndef __lib_pbd_memento_command_h__ #define __lib_pbd_memento_command_h__ -#include -#include +#include + +#include "pbd/command.h" +#include "pbd/stacktrace.h" +#include "pbd/xml++.h" +#include "pbd/demangle.h" + #include +#include -/** 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. +/** 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 used for + * + * 1. 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. + * + * 2. Crossfades; unlike regions, these are completely removed from a session + * when they are deleted. This means that the undo record can contain + * references to non-existant crossfades. To get around this, CrossfadeBinder + * can do `just-in-time' binding from the crossfade ID. */ template -class MementoCommand : public Command +class MementoCommandBinder : public PBD::Destructible { - public: - MementoCommand(obj_T &obj, - XMLNode &before, - XMLNode &after - ) - : obj(obj), before(before), after(after) {} - void operator() () { obj.set_state(after); } - void undo() { obj.set_state(before); } - virtual XMLNode &get_state() - { - XMLNode *node = new XMLNode("MementoCommand"); - node->add_property("obj_id", obj.id().to_s()); - node->add_child_nocopy(before); - node->add_child_nocopy(after); - return *node; - } - // TODO does this need a copy constructor? - protected: - obj_T &obj; - XMLNode &before, &after; +public: + /** @return Stateful object to operate on */ + virtual obj_T* get () const = 0; + + /** @return Name of our type */ + virtual std::string type_name () const { + return PBD::demangled_name (*get ()); + } + + /** Add our own state to an XMLNode */ + virtual void add_state (XMLNode *) = 0; }; +/** A simple MementoCommandBinder which binds directly to an object */ template -class MementoUndoCommand : public Command +class SimpleMementoCommandBinder : public MementoCommandBinder { public: - MementoUndoCommand(obj_T &obj, - XMLNode &before) - : obj(obj), before(before) {} - void operator() () { /* noop */ } - void undo() { obj.set_state(before); } - virtual XMLNode &get_state() - { - XMLNode *node = new XMLNode("MementoUndoCommand"); - node->add_property("obj_id", obj.id().to_s()); - node->add_child_nocopy(before); - return *node; - } -protected: - obj_T &obj; - XMLNode &before; + SimpleMementoCommandBinder (obj_T& o) + : _object (o) + { + _object.Destroyed.connect_same_thread (_object_death_connection, boost::bind (&SimpleMementoCommandBinder::object_died, this)); + } + + obj_T* get () const { + return &_object; + } + + void add_state (XMLNode* node) { + node->add_property ("obj_id", _object.id().to_s()); + } + + void object_died () { + /* The object we are binding died, so drop references to ourselves */ + 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. + */ template -class MementoRedoCommand : public Command +class MementoCommand : public Command { public: - MementoRedoCommand(obj_T &obj, - XMLNode &after) - : obj(obj), after(after) {} - void operator() () { obj.set_state(after); } - void undo() { /* noop */ } - virtual XMLNode &get_state() - { - XMLNode *node = new XMLNode("MementoRedoCommand"); - node->add_property("obj_id", obj.id().to_s()); - node->add_child_nocopy(after); - return *node; - } + MementoCommand (obj_T& a_object, XMLNode* a_before, XMLNode* a_after) + : _binder (new SimpleMementoCommandBinder (a_object)), before (a_before), after (a_after) + { + /* The binder's object died, so we must die */ + _binder->DropReferences.connect_same_thread (_binder_death_connection, boost::bind (&MementoCommand::binder_dying, this)); + } + + MementoCommand (MementoCommandBinder* b, XMLNode* a_before, XMLNode* a_after) + : _binder (b), before (a_before), after (a_after) + { + /* The binder's object died, so we must die */ + _binder->DropReferences.connect_same_thread (_binder_death_connection, boost::bind (&MementoCommand::binder_dying, this)); + } + + ~MementoCommand () { + drop_references (); + delete before; + delete after; + delete _binder; + } + + void binder_dying () { + delete this; + } + + void operator() () { + if (after) { + _binder->get()->set_state(*after, Stateful::current_state_version); + } + } + + void undo() { + if (before) { + _binder->get()->set_state(*before, Stateful::current_state_version); + } + } + + virtual XMLNode &get_state() { + std::string name; + if (before && after) { + name = "MementoCommand"; + } else if (before) { + name = "MementoUndoCommand"; + } else { + name = "MementoRedoCommand"; + } + + XMLNode* node = new XMLNode(name); + _binder->add_state (node); + + node->add_property ("type_name", _binder->type_name ()); + + if (before) { + node->add_child_copy(*before); + } + + if (after) { + node->add_child_copy(*after); + } + + return *node; + } + protected: - obj_T &obj; - XMLNode &after; + MementoCommandBinder* _binder; + XMLNode* before; + XMLNode* after; + PBD::ScopedConnection _binder_death_connection; }; #endif // __lib_pbd_memento_h__