Fix crossfade undo using the stateful diff system. Fixes #3257.
authorCarl Hetherington <carl@carlh.net>
Thu, 26 Aug 2010 01:44:11 +0000 (01:44 +0000)
committerCarl Hetherington <carl@carlh.net>
Thu, 26 Aug 2010 01:44:11 +0000 (01:44 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7694 d708f5d6-7413-0410-9779-e7cbd77b26cf

21 files changed:
gtk2_ardour/crossfade_edit.cc
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/route_time_axis.cc
libs/ardour/ardour/audioplaylist.h
libs/ardour/ardour/crossfade.h
libs/ardour/ardour/playlist.h
libs/ardour/ardour/session.h
libs/ardour/audio_playlist.cc
libs/ardour/crossfade.cc
libs/ardour/globals.cc
libs/ardour/playlist.cc
libs/ardour/session_state.cc
libs/ardour/wscript
libs/pbd/pbd/command.h
libs/pbd/pbd/property_basics.h
libs/pbd/pbd/sequence_property.h
libs/pbd/pbd/stateful.h
libs/pbd/pbd/stateful_diff_command.h
libs/pbd/stateful.cc
libs/pbd/stateful_diff_command.cc

index b6dcf07920be36a332df1ecec14569e3fec8a3fe..49e5ee0609a9ffbd40ecfe59b191a0b4566f2e78 100644 (file)
@@ -788,10 +788,10 @@ CrossfadeEditor::apply ()
        _session->begin_reversible_command (_("Edit crossfade"));
 
        XMLNode& before = xfade->get_state ();
-
+       
        _apply_to (xfade);
 
-       _session->add_command (new MementoCommand<Crossfade> (*xfade.get(), &before, &xfade->get_state()));
+       _session->add_command (new MementoCommand<Crossfade> (*xfade.get(), &before, &xfade->get_state ()));
        _session->commit_reversible_command ();
 }
 
index f2acf2e7a8e44a43ea151d16efebf065e272cf79..cef27660a6882edc7c821a6b0d3f96600c11658b 100644 (file)
@@ -1037,6 +1037,15 @@ RegionMoveDrag::finished_no_copy (
                                playlist->freeze ();
                        }
 
+                       /* this movement may result in a crossfade being modified, so we need to get undo
+                          data from the playlist as well as the region.
+                       */
+                       
+                       r = modified_playlists.insert (playlist);
+                       if (r.second) {
+                               playlist->clear_changes ();
+                       }
+
                        rv->region()->set_position (where, (void*) this);
 
                        _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
@@ -1172,7 +1181,12 @@ void
 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
 {
        for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
-               _editor->session()->add_command (new StatefulDiffCommand (*i));
+               StatefulDiffCommand* c = new StatefulDiffCommand (*i);
+               if (!c->empty()) {
+                       _editor->session()->add_command (new StatefulDiffCommand (*i));
+               } else {
+                       delete c;
+               }
        }
 }
 
index f4c63531cc6817d491d240dcc167cc2806072f50..d540134f17df1dd7b1b0946910a5af0026c73aaf 100644 (file)
@@ -2820,11 +2820,9 @@ Editor::separate_regions_between (const TimeSelection& ts)
                                                         
                                                         /* pick up changes to existing regions */
 
-                                                        vector<StatefulDiffCommand*> cmds;
+                                                        vector<Command*> cmds;
                                                         playlist->rdiff (cmds);
-                                                        for (vector<StatefulDiffCommand*>::iterator j = cmds.begin(); j != cmds.end(); ++j) {
-                                                                _session->add_command (*j);
-                                                        }
+                                                       _session->add_commands (cmds);
 
                                                         /* pick up changes to the playlist itself (adds/removes)
                                                          */
@@ -3773,11 +3771,9 @@ Editor::bounce_range_selection (bool replace, bool enable_processing)
                        playlist->add_region (r, start);
                }
 
-               vector<StatefulDiffCommand*> cmds;
+               vector<Command*> cmds;
                playlist->rdiff (cmds);
-               for (vector<StatefulDiffCommand*>::iterator j = cmds.begin(); j != cmds.end(); ++j) {
-                       _session->add_command (*j);
-               }
+               _session->add_commands (cmds);
 
                 _session->add_command (new StatefulDiffCommand (playlist));
        }
@@ -4492,13 +4488,10 @@ Editor::nudge_track (bool use_edit, bool forwards)
 
                playlist->nudge_after (start, distance, forwards);
                 
-                vector<StatefulDiffCommand*> cmds;
+                vector<Command*> cmds;
 
                 playlist->rdiff (cmds);
-
-                for (vector<StatefulDiffCommand*>::iterator c = cmds.begin(); c != cmds.end(); ++c) {
-                        _session->add_command (*c);
-                }
+               _session->add_commands (cmds);
 
                 _session->add_command (new StatefulDiffCommand (playlist));
        }
@@ -6568,14 +6561,10 @@ Editor::insert_time (nframes64_t pos, nframes64_t frames, InsertTimeOption opt,
 
                        pl->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
 
-                        vector<StatefulDiffCommand*> cmds;
-                        
+                        vector<Command*> cmds;
                         pl->rdiff (cmds);
-                        
-                        for (vector<StatefulDiffCommand*>::iterator c = cmds.begin(); c != cmds.end(); ++c) {
-                                _session->add_command (*c);
-                        }
-                        
+                       _session->add_commands (cmds);
+                       
                        _session->add_command (new StatefulDiffCommand (pl));
                        commit = true;
                }
index 391b08eac68c913e45d2e1f680ab00ac9ffa979f..abc8101195a2e1291ba4d75b48730a85f3457aa2 100644 (file)
@@ -1339,13 +1339,10 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
                if ((what_we_got = playlist->cut (time)) != 0) {
                        _editor.get_cut_buffer().add (what_we_got);
 
-                        vector<StatefulDiffCommand*> cmds;
-                        
+                        vector<Command*> cmds;
                         playlist->rdiff (cmds);
-                        
-                        for (vector<StatefulDiffCommand*>::iterator c = cmds.begin(); c != cmds.end(); ++c) {
-                                _session->add_command (*c);
-                        }
+                        _session->add_commands (cmds);
+                       
                         _session->add_command (new StatefulDiffCommand (playlist));
                }
                break;
@@ -1357,13 +1354,10 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
 
        case Clear:
                if ((what_we_got = playlist->cut (time)) != 0) {
-                        vector<StatefulDiffCommand*> cmds;
-                        
+
+                        vector<Command*> cmds;
                         playlist->rdiff (cmds);
-                        
-                        for (vector<StatefulDiffCommand*>::iterator c = cmds.begin(); c != cmds.end(); ++c) {
-                                _session->add_command (*c);
-                        }
+                       _session->add_commands (cmds);
                         _session->add_command (new StatefulDiffCommand (playlist));
                        what_we_got->release ();
                }
index aee5fb3f649f30ed1007a12824b115505e5abdbf..f2d60bce8bdc7fccc7fe007043577b3b850aa522 100644 (file)
@@ -33,12 +33,39 @@ class Region;
 class AudioRegion;
 class Source;
 
+namespace Properties {
+        /* fake the type, since crossfades are handled by SequenceProperty which doesn't
+           care about such things.
+        */
+        extern PBD::PropertyDescriptor<bool> crossfades;
+}
+
+class AudioPlaylist;   
+
+class CrossfadeListProperty : public PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > >
+{
+public:
+       CrossfadeListProperty (AudioPlaylist &);
+
+       void get_content_as_xml (boost::shared_ptr<Crossfade>, XMLNode &) const;
+       boost::shared_ptr<Crossfade> get_content_from_xml (XMLNode const &) const;
+
+private:
+       CrossfadeListProperty* clone () const;
+       CrossfadeListProperty* create () const;
+
+        friend class AudioPlaylist;
+        /* we live and die with our playlist, no lifetime management needed */
+        AudioPlaylist& _playlist;
+};
+
+       
 class AudioPlaylist : public ARDOUR::Playlist
 {
-  public:
+public:
        typedef std::list<boost::shared_ptr<Crossfade> > Crossfades;
+       static void make_property_quarks ();
 
-   public:
        AudioPlaylist (Session&, const XMLNode&, bool hidden = false);
        AudioPlaylist (Session&, std::string name, bool hidden = false);
        AudioPlaylist (boost::shared_ptr<const AudioPlaylist>, std::string name, bool hidden = false);
@@ -59,6 +86,8 @@ class AudioPlaylist : public ARDOUR::Playlist
 
        bool destroy_region (boost::shared_ptr<Region>);
 
+       void update (const CrossfadeListProperty::ChangeRecord &);
+
     protected:
 
        /* playlist "callbacks" */
@@ -72,7 +101,7 @@ class AudioPlaylist : public ARDOUR::Playlist
         void remove_dependents (boost::shared_ptr<Region> region);
 
     private:
-       Crossfades      _crossfades;
+       CrossfadeListProperty _crossfades;
        Crossfades      _pending_xfade_adds;
 
        void crossfade_invalidated (boost::shared_ptr<Region>);
index 4c75a0226be3b4ee088d84e9339b409c9c74f259..2ce504eacfc8d0190ec9e8cf897f1e7e80101a23 100644 (file)
@@ -73,7 +73,7 @@ class Crossfade : public ARDOUR::AudioRegion
 
        /* the usual XML constructor */
 
-       Crossfade (const Playlist&, XMLNode&);
+       Crossfade (const Playlist&, XMLNode const &);
        virtual ~Crossfade();
 
        static void make_property_quarks ();
index 6473211126480d4cc6b3cded77e4b36e28cbcaf5..a1a7bb752a071ef2df86bad2aeb1fd3b83933507 100644 (file)
@@ -64,8 +64,8 @@ class RegionListProperty : public PBD::SequenceProperty<std::list<boost::shared_
         RegionListProperty (Playlist&);
 
        RegionListProperty* clone () const;
-
-        boost::shared_ptr<Region> lookup_id (const PBD::ID& id) const;
+       void get_content_as_xml (boost::shared_ptr<Region>, XMLNode &) const;
+       boost::shared_ptr<Region> get_content_from_xml (XMLNode const &) const;
 
   private:
        RegionListProperty* create () const;
@@ -90,7 +90,7 @@ public:
 
         void update (const RegionListProperty::ChangeRecord&);
         void clear_owned_changes ();
-        void rdiff (std::vector<PBD::StatefulDiffCommand*>&) const;
+        void rdiff (std::vector<Command*>&) const;
 
        boost::shared_ptr<Region> region_by_id (const PBD::ID&) const;
 
index c408714c94728b4a0e63aca37d7c7467a3ed8b1b..75b9fb1831cc289de714b8a3b0f153696ca2094f 100644 (file)
@@ -680,6 +680,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
                _current_trans.top()->add_command (cmd);
        }
 
+       void add_commands (std::vector<Command*> const & cmds);
+
        std::map<PBD::ID,PBD::StatefulDestructible*> registry;
 
        // these commands are implemented in libs/ardour/session_command.cc
index cfc5a4028adec43358b715fd7ede531425944581..2ac3edff80d755999f9b8fdb19c01bdec5acc3ad 100644 (file)
@@ -21,7 +21,6 @@
 
 #include <cstdlib>
 
-
 #include "ardour/types.h"
 #include "ardour/debug.h"
 #include "ardour/configuration.h"
@@ -38,14 +37,70 @@ using namespace ARDOUR;
 using namespace std;
 using namespace PBD;
 
+namespace ARDOUR {
+       namespace Properties {
+               PBD::PropertyDescriptor<bool> crossfades;
+       }
+}
+
+void
+AudioPlaylist::make_property_quarks ()
+{
+        Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
+}
+
+CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
+        : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
+        , _playlist (pl)
+{
+       
+}
+
+
+CrossfadeListProperty *
+CrossfadeListProperty::create () const
+{
+       return new CrossfadeListProperty (_playlist);
+}
+
+CrossfadeListProperty *
+CrossfadeListProperty::clone () const
+{
+       return new CrossfadeListProperty (*this);
+}
+
+void
+CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
+{
+       /* Crossfades are not written to any state when they are no
+          longer in use, so we must write their state here.
+       */
+
+       XMLNode& c = xfade->get_state ();
+       node.add_child_nocopy (c);
+}
+
+boost::shared_ptr<Crossfade>
+CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
+{
+       XMLNodeList const c = node.children ();
+       assert (c.size() == 1);
+       return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
+}
+
+
 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
        : Playlist (session, node, DataType::AUDIO, hidden)
+       , _crossfades (*this)
 {
 #ifndef NDEBUG
        const XMLProperty* prop = node.property("type");
        assert(!prop || DataType(prop->value()) == DataType::AUDIO);
 #endif
 
+       add_property (_crossfades);
+
        in_set_state++;
        set_state (node, Stateful::loading_state_version);
        in_set_state--;
@@ -53,12 +108,17 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden
 
 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
        : Playlist (session, name, DataType::AUDIO, hidden)
+       , _crossfades (*this)
 {
+       add_property (_crossfades);
 }
 
 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
        : Playlist (other, name, hidden)
+       , _crossfades (*this)
 {
+       add_property (_crossfades);
+       
        RegionList::const_iterator in_o  = other->regions.begin();
        RegionList::iterator in_n = regions.begin();
 
@@ -99,7 +159,10 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
 
 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nframes_t start, nframes_t cnt, string name, bool hidden)
        : Playlist (other, start, cnt, name, hidden)
+       , _crossfades (*this)
 {
+       add_property (_crossfades);
+       
        /* this constructor does NOT notify others (session) */
 }
 
@@ -795,3 +858,13 @@ AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossf
                s (*i);
        }
 }
+
+void
+AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
+{
+       for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
+               add_crossfade (*i);
+       }
+
+       /* don't remove crossfades here; they will be dealt with by the dependency code */
+}
index 601ea86f6b4cb24183fbd7cc5fccac4ba84a1cbf..eb1148fa2cdcf1044be1736683cba99c8ae8e2a2 100644 (file)
@@ -124,7 +124,7 @@ Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioR
        initialize ();
 }
 
-Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
+Crossfade::Crossfade (const Playlist& playlist, XMLNode const & node)
        : AudioRegion (playlist.session(), 0, 0, "unnamed crossfade")
        , CROSSFADE_DEFAULT_PROPERTIES
        , _fade_in (Evoral::Parameter(FadeInAutomation)) // linear (gain coefficient) => -inf..+6dB
@@ -132,7 +132,7 @@ Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
 
 {
        boost::shared_ptr<Region> r;
-       XMLProperty* prop;
+       XMLProperty const * prop;
        LocaleGuard lg (X_("POSIX"));
 
        /* we have to find the in/out regions before we can do anything else */
index 2ecea550b5c400e02bf19738cd4260251d52b5b3..0e12883d8235d992f11a157d6196e5fd3597c611 100644 (file)
@@ -69,7 +69,7 @@
 #include "ardour/debug.h"
 #include "ardour/filesystem_paths.h"
 #include "ardour/mix.h"
-#include "ardour/playlist.h"
+#include "ardour/audioplaylist.h"
 #include "ardour/plugin_manager.h"
 #include "ardour/process_thread.h"
 #include "ardour/profile.h"
@@ -251,6 +251,7 @@ ARDOUR::init (bool use_vst, bool try_optimization)
        AudioRegion::make_property_quarks ();
        RouteGroup::make_property_quarks ();
         Playlist::make_property_quarks ();
+        AudioPlaylist::make_property_quarks ();
 
        /* this is a useful ready to use PropertyChange that many
           things need to check. This avoids having to compose
index 93a4d520e8b6ce31dd4777650202b7539b1a7bb8..1b2d64babab96598b2990f0f0cfd5543d7f67057 100644 (file)
@@ -110,28 +110,46 @@ RegionListProperty::RegionListProperty (Playlist& pl)
         : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
         , _playlist (pl)
 {
+       
 }
 
-boost::shared_ptr<Region>
-RegionListProperty::lookup_id (const ID& id) const
+RegionListProperty *
+RegionListProperty::clone () const
 {
-        boost::shared_ptr<Region> ret =  _playlist.region_by_id (id);
-        
-        if (!ret) {
-                ret = RegionFactory::region_by_id (id);
-        }
+       return new RegionListProperty (*this);
+}
 
-        return ret;
+RegionListProperty *
+RegionListProperty::create () const
+{
+       return new RegionListProperty (_playlist);
 }
 
-RegionListProperty* RegionListProperty::clone () const
+void
+RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
 {
-       return new RegionListProperty (*this);
+       /* All regions (even those which are deleted) have their state saved by other
+          code, so we can just store ID here.
+       */
+       
+       node.add_property ("id", region->id().to_s ());
 }
 
-RegionListProperty* RegionListProperty::create () const
+boost::shared_ptr<Region>
+RegionListProperty::get_content_from_xml (XMLNode const & node) const
 {
-       return new RegionListProperty (_playlist);
+       XMLProperty const * prop = node.property ("id");
+       assert (prop);
+
+       PBD::ID id (prop->value ());
+
+        boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
+        
+        if (!ret) {
+                ret = RegionFactory::region_by_id (id);
+        }
+
+        return ret;
 }
 
 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
@@ -2100,7 +2118,7 @@ Playlist::mark_session_dirty ()
 }
 
 void
-Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
+Playlist::rdiff (vector<Command*>& cmds) const
 {
        RegionLock rlock (const_cast<Playlist *> (this));
        Stateful::rdiff (cmds);
index 31282a210f761766543e1e7a0cfbf934c610899c..225b7c1909fb05253e8d9bd9ba30be97dd56a733 100644 (file)
@@ -2312,6 +2312,14 @@ Session::finish_reversible_command (UndoTransaction& ut)
        _history.add (&ut);
 }
 
+void
+Session::add_commands (vector<Command*> const & cmds)
+{
+       for (vector<Command*>::const_iterator i = cmds.begin(); i != cmds.end(); ++i) {
+               add_command (*i);
+       }
+}
+
 void
 Session::begin_reversible_command(const string& name)
 {
index 051054c9a738faccb55753aacd1c3bf601a97724..f877f3c3beeed16207aa500eba1bd96a7b8f52a5 100644 (file)
@@ -286,7 +286,7 @@ def build(bld):
        obj.includes     = ['.', '../surfaces/control_protocol', '..']
        obj.name         = 'libardour'
        obj.target       = 'ardour'
-       obj.uselib       = 'GLIBMM GTHREAD AUBIO SIGCPP XML UUID JACK SNDFILE SAMPLERATE LRDF OSX COREAUDIO'
+       obj.uselib       = 'GLIBMM GTHREAD AUBIO SIGCPP XML UUID JACK SNDFILE SAMPLERATE LRDF OSX COREAUDIO CURL DL'
        obj.uselib_local = 'libpbd libmidipp libevoral libvamphost libvampplugin libtaglib librubberband libaudiographer'
        obj.vnum         = LIBARDOUR_LIB_VERSION
        obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')
index c6c3c8d3fd21b02d818be71e00fbdc4e150aa990..db4d2bbd814774d0910b1f59786c25a6d21f43c2 100644 (file)
@@ -43,6 +43,10 @@ public:
        virtual XMLNode &get_state();
        virtual int set_state(const XMLNode&, int /*version*/) { /* noop */ return 0; }
 
+       virtual bool empty () const {
+               return false;
+       }
+
 protected:
        Command() {}
        Command(const std::string& name) : _name(name) {}
index cfae36df2042d3b030cc4ce0e7d467e950a86c78..145e84abfcf04e148353dcf2ebdd11512b5f07aa 100644 (file)
@@ -103,7 +103,7 @@ public:
        virtual void get_changes_as_properties (PropertyList& changes, Command *) const = 0;
 
        /** Collect StatefulDiffCommands for changes to anything that we own */
-       virtual void rdiff (std::vector<StatefulDiffCommand*> &) const {}
+       virtual void rdiff (std::vector<Command*> &) const {}
 
        /** Look in an XML node written by get_changes_as_xml and, if XML from this property
         *  is found, create a property with the changes from the XML.
index 5b37d7a0fc6ef3af330c76589391f47df9f37b86..4b494d4a8ddf76c4a012be22932c60ebbbe0e5e1 100644 (file)
@@ -31,6 +31,7 @@
 #include "pbd/id.h"
 #include "pbd/property_basics.h"
 #include "pbd/property_list.h"
+#include "pbd/stateful_diff_command.h"
 
 namespace PBD {
 
@@ -80,8 +81,6 @@ class SequenceProperty : public PropertyBase
        SequenceProperty (PropertyID id, const boost::function<void(const ChangeRecord&)>& update)
                 : PropertyBase (id), _update_callback (update) {}
 
-       virtual typename Container::value_type lookup_id (const PBD::ID&) const = 0;
-
         void invert () {
                _changes.removed.swap (_changes.added);
         }
@@ -97,18 +96,25 @@ class SequenceProperty : public PropertyBase
                        for (typename ChangeContainer::iterator i = _changes.added.begin(); i != _changes.added.end(); ++i) {
                                 XMLNode* add_node = new XMLNode ("Add");
                                 child->add_child_nocopy (*add_node);
-                                add_node->add_property ("id", (*i)->id().to_s());
+                               get_content_as_xml (*i, *add_node);
                        }
                }
                if (!_changes.removed.empty()) {
                        for (typename ChangeContainer::iterator i = _changes.removed.begin(); i != _changes.removed.end(); ++i) {
                                 XMLNode* remove_node = new XMLNode ("Remove");
                                 child->add_child_nocopy (*remove_node);
-                                remove_node->add_property ("id", (*i)->id().to_s());
+                               get_content_as_xml (*i, *remove_node);
                        }
                }
        }
 
+       /** Get a representation of one of our items as XML.  The representation must be sufficient to
+        *  restore the item's state later; an ID is ok if someone else is storing the item state,
+        *  otherwise it needs to be the full state.  The supplied node is an <Add> or <Remove>
+        *  which this method can either add properties or children to.
+        */
+       virtual void get_content_as_xml (typename ChangeContainer::value_type, XMLNode &) const = 0;
+
        bool set_value (XMLNode const &) {
                /* XXX: not used, but probably should be */
                assert (false);
@@ -191,11 +197,10 @@ class SequenceProperty : public PropertyBase
 
                XMLNodeList const & grandchildren = (*i)->children ();
                for (XMLNodeList::const_iterator j = grandchildren.begin(); j != grandchildren.end(); ++j) {
-                       XMLProperty const * prop = (*j)->property ("id");
-                       assert (prop);
-                       PBD::ID id (prop->value ());
-                       typename Container::value_type v = lookup_id (id);
+
+                       typename Container::value_type v = get_content_from_xml (**j);
                        assert (v);
+                       
                        if ((*j)->name() == "Add") {
                                p->_changes.added.insert (v);
                        } else if ((*j)->name() == "Remove") {
@@ -206,13 +211,16 @@ class SequenceProperty : public PropertyBase
                return p;
         }
 
+       /** Given an <Add> or <Remove> node as passed into get_content_to_xml, obtain an item */
+       virtual typename Container::value_type get_content_from_xml (XMLNode const & node) const = 0;
+
        void clear_owned_changes () {
                for (typename Container::iterator i = begin(); i != end(); ++i) {
                        (*i)->clear_changes ();
                }
        }
 
-       void rdiff (std::vector<StatefulDiffCommand*>& cmds) const {
+       void rdiff (std::vector<Command*>& cmds) const {
                for (typename Container::const_iterator i = begin(); i != end(); ++i) {
                        if ((*i)->changed ()) {
                                StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
@@ -255,6 +263,11 @@ class SequenceProperty : public PropertyBase
                return _val.erase (f, l);
        }
 
+       void remove (const typename Container::value_type& v) {
+               _changes.remove (v);
+               _val.remove (v);
+       }
+
        void push_back (const typename Container::value_type& v) {
                _changes.add (v);
                _val.push_back (v);
index 5c1f079bc6d47240e1d9b0d47a9882dbd6545502..735ffbdc4a68ab8f4d97bb340ee1be9549adfebb 100644 (file)
@@ -70,7 +70,7 @@ class Stateful {
        void clear_changes ();
        virtual void clear_owned_changes ();
         PropertyList* get_changes_as_properties (Command *) const;
-       virtual void rdiff (std::vector<StatefulDiffCommand*> &) const;
+       virtual void rdiff (std::vector<Command*> &) const;
         bool changed() const;
 
         /* create a property list from an XMLNode
index 2d5c234d762f7d58963ef888a473e2614afa1d22..2a213d7a17e7fcea73be991cf6d3316e2d921727 100644 (file)
@@ -45,6 +45,8 @@ public:
         
        XMLNode& get_state ();
 
+       bool empty () const;
+
 private:
        boost::weak_ptr<Stateful> _object; ///< the object in question
         PBD::PropertyList* _changes; ///< property changes to execute this command
index 8411dc4e60ec3cd9ac1160a55c73effffa867851..e13a499e414b385eb2165214c53035251050b954 100644 (file)
@@ -340,7 +340,7 @@ Stateful::property_factory (const XMLNode& history_node) const
 }
 
 void
-Stateful::rdiff (vector<StatefulDiffCommand*>& cmds) const
+Stateful::rdiff (vector<Command*>& cmds) const
 {
        for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
                i->second->rdiff (cmds);
index 93a215ad8a711140fb6cae9074df61b422c6a8b4..0f456d2d6e6ac9f042501dd521968bc8f5609fc7 100644 (file)
@@ -118,3 +118,9 @@ StatefulDiffCommand::get_state ()
 
        return *node;
 }
+
+bool
+StatefulDiffCommand::empty () const
+{
+       return _changes->empty();
+}