From db8b575c30845bafc34b87bacd52129c95d1c478 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 2 Mar 2010 00:00:00 +0000 Subject: [PATCH] the mega-properties/SequenceProperty patch. split is broken at present (right hand starts has start-in-source of zero) git-svn-id: svn://localhost/ardour2/branches/3.0@6718 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/editor.cc | 6 +- gtk2_ardour/editor_audio_import.cc | 2 +- gtk2_ardour/editor_drag.cc | 62 +++++- gtk2_ardour/editor_mouse.cc | 14 +- gtk2_ardour/editor_ops.cc | 19 +- gtk2_ardour/editor_selection.cc | 4 +- gtk2_ardour/opts.cc | 2 +- libs/ardour/ardour/debug.h | 59 ++--- libs/ardour/ardour/playlist.h | 46 +++- libs/ardour/ardour/region.h | 2 + libs/ardour/ardour/region_factory.h | 7 +- libs/ardour/ardour/session.h | 5 +- libs/ardour/ardour/session_object.h | 1 + libs/ardour/ardour/session_playlists.h | 5 + libs/ardour/audioregion.cc | 30 ++- libs/ardour/crossfade.cc | 4 +- libs/ardour/debug.cc | 116 ++-------- libs/ardour/globals.cc | 13 +- libs/ardour/midi_playlist.cc | 1 + libs/ardour/midi_ring_buffer.cc | 1 + libs/ardour/midi_ui.cc | 1 + libs/ardour/playlist.cc | 154 ++++++++++++- libs/ardour/region.cc | 121 ++++++---- libs/ardour/region_factory.cc | 28 ++- libs/ardour/route_group.cc | 46 ++-- libs/ardour/session.cc | 18 +- libs/ardour/session_command.cc | 10 +- libs/ardour/session_object.cc | 5 +- libs/ardour/session_playlists.cc | 20 ++ libs/ardour/session_state.cc | 13 +- libs/ardour/source.cc | 1 + libs/pbd/debug.cc | 108 +++++++++ libs/pbd/pbd/debug.h | 57 +++++ libs/pbd/pbd/properties.h | 199 +++++------------ libs/pbd/pbd/property_basics.h | 116 ++++++++++ libs/pbd/pbd/property_basics_impl.h | 53 +++++ libs/pbd/pbd/property_list.h | 75 +++++++ libs/pbd/pbd/property_list_impl.h | 37 ++++ libs/pbd/pbd/sequence_property.h | 291 +++++++++++++++++++++++++ libs/pbd/pbd/stateful.h | 21 +- libs/pbd/pbd/stateful_diff_command.h | 5 +- libs/pbd/property_factory.cc | 53 +++++ libs/pbd/property_list.cc | 69 ++++++ libs/pbd/stateful.cc | 78 ++++--- libs/pbd/stateful_diff_command.cc | 53 ++++- libs/pbd/undo.cc | 1 - libs/pbd/wscript | 3 + libs/surfaces/mackie/mackie_port.cc | 1 + 48 files changed, 1552 insertions(+), 484 deletions(-) create mode 100644 libs/pbd/debug.cc create mode 100644 libs/pbd/pbd/debug.h create mode 100644 libs/pbd/pbd/property_basics.h create mode 100644 libs/pbd/pbd/property_basics_impl.h create mode 100644 libs/pbd/pbd/property_list.h create mode 100644 libs/pbd/pbd/property_list_impl.h create mode 100644 libs/pbd/pbd/sequence_property.h create mode 100644 libs/pbd/property_factory.cc create mode 100644 libs/pbd/property_list.cc diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 88b7586888..1b03576ace 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -3995,7 +3995,7 @@ Editor::new_playlists (TimeAxisView* v) begin_reversible_command (_("new playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.id); + mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id); commit_reversible_command (); } @@ -4011,7 +4011,7 @@ Editor::copy_playlists (TimeAxisView* v) begin_reversible_command (_("copy playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.id); + mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id); commit_reversible_command (); } @@ -4026,7 +4026,7 @@ Editor::clear_playlists (TimeAxisView* v) begin_reversible_command (_("clear playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.id); + mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id); commit_reversible_command (); } diff --git a/gtk2_ardour/editor_audio_import.cc b/gtk2_ardour/editor_audio_import.cc index c70832182a..05056f3829 100644 --- a/gtk2_ardour/editor_audio_import.cc +++ b/gtk2_ardour/editor_audio_import.cc @@ -838,7 +838,7 @@ Editor::finish_bringing_in_material (boost::shared_ptr region, uint32_t } boost::shared_ptr playlist = existing_track->diskstream()->playlist(); - boost::shared_ptr copy (RegionFactory::create (region)); + boost::shared_ptr copy (RegionFactory::create (region, region->properties())); begin_reversible_command (_("insert file")); XMLNode &before = playlist->get_state(); playlist->add_region (copy, pos); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index efcd73ba26..2922ad19bb 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -818,6 +818,7 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) vector copies; boost::shared_ptr ds; boost::shared_ptr from_playlist; + boost::shared_ptr to_playlist; RegionSelection new_views; typedef set > PlaylistSet; PlaylistSet modified_playlists; @@ -828,6 +829,7 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) bool changed_tracks, changed_position; map > final; RouteTimeAxisView* source_tv; + vector sdc; if (!movement_occurred) { /* just a click */ @@ -878,6 +880,8 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) /* make a list of where each region ended up */ final = find_time_axis_views_and_layers (); + cerr << "Iterate over " << _views.size() << " views\n"; + for (list::const_iterator i = _views.begin(); i != _views.end(); ) { RegionView* rv = (*i); @@ -886,6 +890,9 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) nframes64_t where; + from_playlist.reset (); + to_playlist.reset (); + if (rv->region()->locked()) { ++i; continue; @@ -915,7 +922,7 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) if (changed_tracks || _copy) { - boost::shared_ptr to_playlist = dest_rtv->playlist(); + to_playlist = dest_rtv->playlist(); if (!to_playlist) { ++i; @@ -929,10 +936,18 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) insert_result = modified_playlists.insert (to_playlist); if (insert_result.second) { - _editor->session()->add_command (new MementoCommand(*to_playlist, &to_playlist->get_state(), 0)); + to_playlist->clear_history (); } + cerr << "To playlist " << to_playlist->name() << " region history contains " + << to_playlist->region_list().change().added.size() << " adds and " + << to_playlist->region_list().change().removed.size() << " removes\n"; + + cerr << "Adding new region " << new_region->id() << " based on " + << rv->region()->id() << endl; + to_playlist->add_region (new_region, where); + if (dest_rtv->view()->layer_display() == Stacked) { new_region->set_layer (dest_layer); new_region->set_pending_explicit_relayer (true); @@ -977,9 +992,11 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) playlist->freeze(); } + cerr << "Moving region " << rv->region()->id() << endl; + rv->region()->set_position (where, (void*) this); - _editor->session()->add_command (new StatefulDiffCommand (rv->region())); + sdc.push_back (new StatefulDiffCommand (rv->region())); } if (changed_tracks && !_copy) { @@ -1012,9 +1029,15 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) insert_result = modified_playlists.insert (from_playlist); if (insert_result.second) { - _editor->session()->add_command (new MementoCommand(*from_playlist, &from_playlist->get_state(), 0)); + from_playlist->clear_history (); } + + cerr << "From playlist " << from_playlist->name() << " region history contains " + << from_playlist->region_list().change().added.size() << " adds and " + << from_playlist->region_list().change().removed.size() << " removes\n"; + cerr << "removing region " << rv->region() << endl; + from_playlist->remove_region (rv->region()); /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region @@ -1035,7 +1058,16 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) */ if (_views.empty()) { - break; + if (to_playlist) { + sdc.push_back (new StatefulDiffCommand (to_playlist)); + cerr << "Saved diff for to:" << to_playlist->name() << endl; + } + + if (from_playlist && (from_playlist != to_playlist)) { + sdc.push_back (new StatefulDiffCommand (from_playlist)); + cerr << "Saved diff for from:" << from_playlist->name() << endl; + } + break; } else { i = _views.begin(); } @@ -1047,11 +1079,25 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) if (_copy) { copies.push_back (rv); } + + cerr << "Done with TV, top = " << to_playlist << " from = " << from_playlist << endl; + + if (to_playlist) { + sdc.push_back (new StatefulDiffCommand (to_playlist)); + cerr << "Saved diff for to:" << to_playlist->name() << endl; + } + + if (from_playlist && (from_playlist != to_playlist)) { + sdc.push_back (new StatefulDiffCommand (from_playlist)); + cerr << "Saved diff for from:" << from_playlist->name() << endl; + } } + /* if we've created new regions either by copying or moving to a new track, we want to replace the old selection with the new ones */ + if (new_views.size() > 0) { _editor->selection->set (new_views); } @@ -1061,9 +1107,9 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred) } out: - for (set >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) { - _editor->session()->add_command (new MementoCommand(*(*p), 0, &(*p)->get_state())); - } + for (vector::iterator i = sdc.begin(); i != sdc.end(); ++i) { + _editor->session()->add_command (*i); + } _editor->commit_reversible_command (); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 13bdf7af5c..5820218a2a 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -687,14 +687,14 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT switch (item_type) { case FadeInHandleItem: { - RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id); + RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id); _drags->set (new FadeInDrag (this, item, reinterpret_cast (item->get_data("regionview")), s), event); return true; } case FadeOutHandleItem: { - RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id); + RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id); _drags->set (new FadeOutDrag (this, item, reinterpret_cast (item->get_data("regionview")), s), event); return true; } @@ -717,7 +717,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT case RegionViewNameHighlight: { - RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id); + RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id); _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event); return true; break; @@ -726,7 +726,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT case RegionViewName: { /* rename happens on edit clicks */ - RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.id); + RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id); _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event); return true; break; @@ -2484,7 +2484,7 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* if (Config->get_edit_mode() == Splice) { _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer())); } else { - RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false)); } @@ -2499,7 +2499,7 @@ Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionV _region_motion_group->raise_to_top (); - RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true)); } @@ -2512,7 +2512,7 @@ Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, Region return; } - RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false)); begin_reversible_command (_("Drag region brush")); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 2bb91fe229..c3800b21fb 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -169,10 +169,9 @@ Editor::split_regions_at (nframes64_t where, RegionSelection& regions) } if (pl) { - XMLNode &before = pl->get_state(); + pl->clear_history (); pl->split_region ((*a)->region(), where); - XMLNode &after = pl->get_state(); - _session->add_command(new MementoCommand(*pl, &before, &after)); + _session->add_command (new StatefulDiffCommand (pl)); } a = tmp; @@ -4883,7 +4882,7 @@ Editor::brush (nframes64_t pos) void Editor::reset_region_gain_envelopes () { - RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); if (!_session || rs.empty()) { return; @@ -4908,7 +4907,7 @@ Editor::reset_region_gain_envelopes () void Editor::toggle_gain_envelope_visibility () { - RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); if (!_session || rs.empty()) { return; @@ -4932,7 +4931,7 @@ Editor::toggle_gain_envelope_visibility () void Editor::toggle_gain_envelope_active () { - RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); if (!_session || rs.empty()) { return; @@ -4955,7 +4954,7 @@ Editor::toggle_gain_envelope_active () void Editor::toggle_region_lock () { - RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); if (!_session || rs.empty()) { return; @@ -4975,7 +4974,7 @@ Editor::toggle_region_lock () void Editor::set_region_lock_style (Region::PositionLockStyle ps) { - RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); if (!_session || rs.empty()) { return; @@ -4997,7 +4996,7 @@ Editor::set_region_lock_style (Region::PositionLockStyle ps) void Editor::toggle_region_mute () { - RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); if (!_session || rs.empty()) { return; @@ -5017,7 +5016,7 @@ Editor::toggle_region_mute () void Editor::toggle_region_opaque () { - RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.id); + RegionSelection rs = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); if (!_session || rs.empty()) { return; diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 3e5b412c85..5b29d8439f 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -476,7 +476,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, if (press) { if (selection->selected (clicked_routeview)) { - get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.id); + get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id); } else { all_equivalent_regions.push_back (clicked_regionview); } @@ -494,7 +494,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, case Selection::Set: if (!selection->selected (clicked_regionview)) { - get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.id); + get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id); selection->set (all_equivalent_regions); commit = true; } else { diff --git a/gtk2_ardour/opts.cc b/gtk2_ardour/opts.cc index b5075daa2f..429d5fa85b 100644 --- a/gtk2_ardour/opts.cc +++ b/gtk2_ardour/opts.cc @@ -139,7 +139,7 @@ ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[]) break; case 'D': - if (ARDOUR::parse_debug_options (optarg)) { + if (PBD::parse_debug_options (optarg)) { exit (0); } break; diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h index b7ee4c328c..5b4405b7d8 100644 --- a/libs/ardour/ardour/debug.h +++ b/libs/ardour/ardour/debug.h @@ -24,51 +24,28 @@ #include -namespace ARDOUR { - - extern uint64_t debug_bits; - void debug_print (const char* prefix, std::string str); - void set_debug_bits (uint64_t bits); - int parse_debug_options (const char* str); - void list_debug_options (); +#include "pbd/debug.h" +namespace PBD { namespace DEBUG { - - /* this namespace is so that we can write DEBUG::bit_name */ - - enum DebugBits { - MidiSourceIO = 0x1, - MidiPlaylistIO = 0x2, - MidiDiskstreamIO = 0x4, - SnapBBT = 0x8, - Configuration = 0x10, - Latency = 0x20, - Processors = 0x40, - Graph = 0x80, - Destruction = 0x100, - MTC = 0x200, - Transport = 0x400, - Slave = 0x800, - SessionEvents = 0x800, - MidiIO = 0x1000, - MackieControl = 0x2000, - MidiClock = 0x4000, - Properties = 0x8000 - }; + extern uint64_t MidiSourceIO; + extern uint64_t MidiPlaylistIO; + extern uint64_t MidiDiskstreamIO; + extern uint64_t SnapBBT; + extern uint64_t Configuration; + extern uint64_t Latency; + extern uint64_t Processors; + extern uint64_t Graph; + extern uint64_t Destruction; + extern uint64_t MTC; + extern uint64_t Transport; + extern uint64_t Slave; + extern uint64_t SessionEvents; + extern uint64_t MidiIO; + extern uint64_t MackieControl; + extern uint64_t MidiClock; } - } -#ifndef NDEBUG -#define DEBUG_TRACE(bits,str) if ((bits) & ARDOUR::debug_bits) { ARDOUR::debug_print (# bits, str); } -#define DEBUG_STR_DECL(id) std::stringstream __debug_str ## id; -#define DEBUG_STR(id) __debug_str ## id -#define DEBUG_STR_APPEND(id,s) __debug_str ## id << s; -#else -#define DEBUG_TRACE(bits,fmt,...) /*empty*/ -#define DEBUG_STR(a) /* empty */ -#define DEBUG_STR_APPEND(a,b) /* empty */ -#endif - #endif /* __ardour_debug_h__ */ diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 38469f4723..3c79ec0ec4 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -36,6 +36,7 @@ #include "pbd/undo.h" #include "pbd/stateful.h" #include "pbd/statefuldestructible.h" +#include "pbd/sequence_property.h" #include "evoral/types.hpp" @@ -49,11 +50,43 @@ namespace ARDOUR { class Session; class Region; +class Playlist; + +namespace Properties { + /* fake the type, since regions are handled by SequenceProperty which doesn't + care about such things. + */ + extern PBD::PropertyDescriptor regions; +} + +class RegionListProperty : public PBD::SequenceProperty > > +{ + public: + RegionListProperty (Playlist&); + + boost::shared_ptr lookup_id (const PBD::ID& id); + void diff (PBD::PropertyList& before, PBD::PropertyList& after) const; + + private: + friend class Playlist; + std::list > rlist() { return _val; } + + /* we live and die with our playlist, no lifetime management needed */ + Playlist& _playlist; + + /* create a copy of this RegionListProperty that only + has what is needed for use in a history list command. This + means that it won't contain the actual region list but + will have the added/removed list. + */ + RegionListProperty* copy_for_history () const; +}; class Playlist : public SessionObject , public boost::enable_shared_from_this { public: typedef std::list > RegionList; + static void make_property_quarks (); Playlist (Session&, const XMLNode&, DataType type, bool hidden = false); Playlist (Session&, std::string name, DataType type, bool hidden = false); @@ -62,6 +95,13 @@ class Playlist : public SessionObject virtual ~Playlist (); + bool set_property (const PBD::PropertyBase&); + void update (const RegionListProperty::ChangeRecord&); + + PBD::PropertyList* property_factory (const XMLNode&) const; + + boost::shared_ptr region_by_id (const PBD::ID&); + void set_region_ownership (); virtual void clear (bool with_signals=true); @@ -107,7 +147,7 @@ class Playlist : public SessionObject boost::shared_ptr copy (std::list&, bool result_is_hidden = true); int paste (boost::shared_ptr, framepos_t position, float times); - const RegionList& region_list () const { return regions; } + const RegionListProperty& region_list () const { return regions; } RegionList* regions_at (framepos_t frame); RegionList* regions_touched (framepos_t start, framepos_t end); @@ -192,7 +232,7 @@ class Playlist : public SessionObject friend class RegionLock; - RegionList regions; /* the current list of regions in the playlist */ + RegionListProperty regions; /* the current list of regions in the playlist */ std::set > all_regions; /* all regions ever added to this playlist */ PBD::ScopedConnectionList region_state_changed_connections; DataType _type; @@ -277,8 +317,6 @@ class Playlist : public SessionObject virtual XMLNode& state (bool); - boost::shared_ptr region_by_id (PBD::ID); - bool add_region_internal (boost::shared_ptr, framepos_t position); int remove_region_internal (boost::shared_ptr); diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index fb827e308d..1da185c6f2 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -91,6 +91,8 @@ class Region static PBD::Signal2, const PBD::PropertyChange&> RegionPropertyChanged; + PBD::PropertyList* property_factory (const XMLNode&) const; + void unlock_property_changes () { _no_property_changes = false; } void block_property_changes () { _no_property_changes = true; } diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h index 16e48828d4..a01103603a 100644 --- a/libs/ardour/ardour/region_factory.h +++ b/libs/ardour/ardour/region_factory.h @@ -21,6 +21,7 @@ #define __ardour_region_factory_h__ #include +#include #include "pbd/id.h" @@ -37,8 +38,10 @@ class AudioRegion; class RegionFactory { public: + typedef std::map > RegionMap; static boost::shared_ptr region_by_id (const PBD::ID&); + static const RegionMap all_regions() { return region_map; } static void clear_map (); /** This is emitted only when a new id is assigned. Therefore, @@ -75,8 +78,10 @@ class RegionFactory { static boost::shared_ptr create (SourceList& srcs, const XMLNode&); private: - static std::map > region_map; + static Glib::StaticMutex region_map_lock; + static RegionMap region_map; static void map_add (boost::shared_ptr); + static void map_remove (boost::shared_ptr); }; } diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index edeeeb11ef..d26c84a795 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -501,6 +501,9 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi /* region info */ + boost::shared_ptr region_by_id (const PBD::ID&) const; + boost::shared_ptr find_whole_file_parent (boost::shared_ptr) const; + void add_regions (std::vector >&); PBD::Signal1 > RegionAdded; @@ -511,8 +514,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi std::string new_region_name (std::string); std::string path_from_region_name (DataType type, std::string name, std::string identifier); - boost::shared_ptr find_whole_file_parent (boost::shared_ptr); - boost::shared_ptr XMLRegionFactory (const XMLNode&, bool full); boost::shared_ptr XMLAudioRegionFactory (const XMLNode&, bool full); boost::shared_ptr XMLMidiRegionFactory (const XMLNode&, bool full); diff --git a/libs/ardour/ardour/session_object.h b/libs/ardour/ardour/session_object.h index d18a1f9d41..3e401a66ce 100644 --- a/libs/ardour/ardour/session_object.h +++ b/libs/ardour/ardour/session_object.h @@ -23,6 +23,7 @@ #include #include "pbd/statefuldestructible.h" #include "pbd/signals.h" +#include "pbd/properties.h" #include "ardour/ardour.h" #include "ardour/session_handle.h" diff --git a/libs/ardour/ardour/session_playlists.h b/libs/ardour/ardour/session_playlists.h index db29ee8c05..d654595d60 100644 --- a/libs/ardour/ardour/session_playlists.h +++ b/libs/ardour/ardour/session_playlists.h @@ -31,6 +31,10 @@ class XMLNode; +namespace PBD { + class ID; +} + namespace ARDOUR { class Playlist; @@ -44,6 +48,7 @@ public: ~SessionPlaylists (); boost::shared_ptr by_name (std::string name); + boost::shared_ptr by_id (const PBD::ID&); uint32_t source_use_count (boost::shared_ptr src) const; template void foreach (T *obj, void (T::*func)(boost::shared_ptr)); void get (std::vector >&); diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 1288dcbb86..e30a3a5994 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -67,12 +67,18 @@ namespace ARDOUR { void AudioRegion::make_property_quarks () { - Properties::envelope_active.id = g_quark_from_static_string (X_("envelope-active")); - Properties::default_fade_in.id = g_quark_from_static_string (X_("default-fade-in")); - Properties::default_fade_out.id = g_quark_from_static_string (X_("default-fade-out")); - Properties::fade_in_active.id = g_quark_from_static_string (X_("fade-in-active")); - Properties::fade_out_active.id = g_quark_from_static_string (X_("fade-out-active")); - Properties::scale_amplitude.id = g_quark_from_static_string (X_("scale-amplitude")); + Properties::envelope_active.property_id = g_quark_from_static_string (X_("envelope-active")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for envelope-active = %1\n", Properties::envelope_active.property_id)); + Properties::default_fade_in.property_id = g_quark_from_static_string (X_("default-fade-in")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for default-fade-in = %1\n", Properties::default_fade_in.property_id)); + Properties::default_fade_out.property_id = g_quark_from_static_string (X_("default-fade-out")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for default-fade-out = %1\n", Properties::default_fade_out.property_id)); + Properties::fade_in_active.property_id = g_quark_from_static_string (X_("fade-in-active")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-active = %1\n", Properties::fade_in_active.property_id)); + Properties::fade_out_active.property_id = g_quark_from_static_string (X_("fade-out-active")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-active = %1\n", Properties::fade_out_active.property_id)); + Properties::scale_amplitude.property_id = g_quark_from_static_string (X_("scale-amplitude")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for scale-amplitude = %1\n", Properties::scale_amplitude.property_id)); } void @@ -690,37 +696,37 @@ AudioRegion::set_property (const PropertyBase& prop) { DEBUG_TRACE (DEBUG::Properties, string_compose ("audio region %1 set property %2\n", _name.val(), prop.property_name())); - if (prop == Properties::envelope_active.id) { + if (prop == Properties::envelope_active.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _envelope_active) { _envelope_active = val; return true; } - } else if (prop == Properties::default_fade_in.id) { + } else if (prop == Properties::default_fade_in.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _default_fade_in) { _default_fade_in = val; return true; } - } else if (prop == Properties::default_fade_out.id) { + } else if (prop == Properties::default_fade_out.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _default_fade_out) { _default_fade_out = val; return true; } - } else if (prop == Properties::fade_in_active.id) { + } else if (prop == Properties::fade_in_active.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _fade_in_active) { _fade_in_active = val; return true; } - } else if (prop == Properties::fade_out_active.id) { + } else if (prop == Properties::fade_out_active.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _fade_out_active) { _fade_out_active = val; return true; } - } else if (prop == Properties::scale_amplitude.id) { + } else if (prop == Properties::scale_amplitude.property_id) { gain_t val = dynamic_cast*>(&prop)->val(); if (val != _scale_amplitude) { _scale_amplitude = val; diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 8275876fe8..4f20db5b35 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -20,6 +20,7 @@ #include "pbd/stacktrace.h" +#include "ardour/debug.h" #include "ardour/types.h" #include "ardour/crossfade.h" #include "ardour/crossfade_compare.h" @@ -60,7 +61,8 @@ namespace ARDOUR { void Crossfade::make_property_quarks () { - Properties::follow_overlap.id = g_quark_from_static_string (X_("follow-overlap")); + Properties::follow_overlap.property_id = g_quark_from_static_string (X_("follow-overlap")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for follow-overlap = %1\n", Properties::follow_overlap.property_id)); } void diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc index 48edc9a8d5..d169850694 100644 --- a/libs/ardour/debug.cc +++ b/libs/ardour/debug.cc @@ -23,106 +23,22 @@ #include "ardour/debug.h" -#include "i18n.h" - using namespace std; -void -ARDOUR::debug_print (const char* prefix, string str) -{ - cerr << prefix << ": " << str; -} +uint64_t PBD::DEBUG::MidiSourceIO = PBD::new_debug_bit ("midisourceio"); +uint64_t PBD::DEBUG::MidiPlaylistIO = PBD::new_debug_bit ("midiplaylistio"); +uint64_t PBD::DEBUG::MidiDiskstreamIO = PBD::new_debug_bit ("mididiskstreamio"); +uint64_t PBD::DEBUG::SnapBBT = PBD::new_debug_bit ("snapbbt"); +uint64_t PBD::DEBUG::Configuration = PBD::new_debug_bit ("configuration"); +uint64_t PBD::DEBUG::Latency = PBD::new_debug_bit ("latency"); +uint64_t PBD::DEBUG::Processors = PBD::new_debug_bit ("processors"); +uint64_t PBD::DEBUG::Graph = PBD::new_debug_bit ("graph"); +uint64_t PBD::DEBUG::Destruction = PBD::new_debug_bit ("destruction"); +uint64_t PBD::DEBUG::MTC = PBD::new_debug_bit ("mtc"); +uint64_t PBD::DEBUG::Transport = PBD::new_debug_bit ("transport"); +uint64_t PBD::DEBUG::Slave = PBD::new_debug_bit ("slave"); +uint64_t PBD::DEBUG::SessionEvents = PBD::new_debug_bit ("sessionevents"); +uint64_t PBD::DEBUG::MidiIO = PBD::new_debug_bit ("midiio"); +uint64_t PBD::DEBUG::MackieControl = PBD::new_debug_bit ("mackiecontrol"); +uint64_t PBD::DEBUG::MidiClock = PBD::new_debug_bit ("midiclock"); -void -ARDOUR::set_debug_bits (uint64_t bits) -{ - debug_bits = bits; -} - -int -ARDOUR::parse_debug_options (const char* str) -{ - char* p; - char* sp; - uint64_t bits = 0; - char* copy = strdup (str); - - p = strtok_r (copy, ",", &sp); - - while (p) { - if (strcasecmp (p, "list") == 0) { - list_debug_options (); - free (copy); - return 1; - } - - if (strcasecmp (p, "all") == 0) { - ARDOUR::set_debug_bits (~0ULL); - free (copy); - return 0; - } - - if (strncasecmp (p, "midisourceio", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::MidiSourceIO; - } else if (strncasecmp (p, "midiplaylistio", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::MidiPlaylistIO; - } else if (strncasecmp (p, "mididiskstreamio", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::MidiDiskstreamIO; - } else if (strncasecmp (p, "snapbbt", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::SnapBBT; - } else if (strncasecmp (p, "configuration", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Configuration; - } else if (strncasecmp (p, "latency", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Latency; - } else if (strncasecmp (p, "processors", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Processors; - } else if (strncasecmp (p, "graph", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Graph; - } else if (strncasecmp (p, "destruction", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Destruction; - } else if (strncasecmp (p, "mtc", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::MTC; - } else if (strncasecmp (p, "transport", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Transport; - } else if (strncasecmp (p, "slave", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Slave; - } else if (strncasecmp (p, "sessionevents", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::SessionEvents; - } else if (strncasecmp (p, "midiio", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::MidiIO; - } else if (strncasecmp (p, "midiclock", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::MidiClock; - } else if (strncasecmp (p, "properties", strlen (p)) == 0) { - bits |= ARDOUR::DEBUG::Properties; - } - - p = strtok_r (0, ",", &sp); - } - - free (copy); - ARDOUR::set_debug_bits (bits); - return 0; -} - -void -ARDOUR::list_debug_options () -{ - cerr << _("The following debug options are available. Separate multipe options with commas.\nNames are case-insensitive and can be abbreviated.") << endl << endl; - cerr << "\tAll" << endl; - cerr << "\tMidiSourceIO" << endl; - cerr << "\tMidiPlaylistIO" << endl; - cerr << "\tMidiDiskstreamIO" << endl; - cerr << "\tSnapBBT" << endl; - cerr << "\tConfiguration" << endl; - cerr << "\tLatency" << endl; - cerr << "\tGraph" << endl; - cerr << "\tDestruction" << endl; - cerr << "\tMTC" << endl; - cerr << "\tTransport" << endl; - cerr << "\tSlave" << endl; - cerr << "\tSessionEvents" << endl; - cerr << "\tMidiIO" << endl; - cerr << "\tLatencyCompensation" << endl; - cerr << "\tMidiClock" << endl; - cerr << "\tProperties" << endl; -} diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index ee19f1caac..389bcc196c 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -68,6 +68,7 @@ #include "ardour/debug.h" #include "ardour/filesystem_paths.h" #include "ardour/mix.h" +#include "ardour/playlist.h" #include "ardour/plugin_manager.h" #include "ardour/profile.h" #include "ardour/region.h" @@ -95,8 +96,6 @@ using namespace ARDOUR; using namespace std; using namespace PBD; -uint64_t ARDOUR::debug_bits = 0x0; - MIDI::Port *ARDOUR::default_mmc_port = 0; MIDI::Port *ARDOUR::default_mtc_port = 0; MIDI::Port *ARDOUR::default_midi_port = 0; @@ -137,9 +136,12 @@ namespace ARDOUR { void ARDOUR::make_property_quarks () { - Properties::fade_in.id = g_quark_from_static_string (X_("fade_in_FAKE")); - Properties::fade_out.id = g_quark_from_static_string (X_("fade_out_FAKE")); - Properties::envelope.id = g_quark_from_static_string (X_("envelope_FAKE")); + Properties::fade_in.property_id = g_quark_from_static_string (X_("fade_in_FAKE")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade_in_FAKE = %1\n", Properties::fade_in.property_id)); + Properties::fade_out.property_id = g_quark_from_static_string (X_("fade_out_FAKE")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade_out_FAKE = %1\n", Properties::fade_out.property_id)); + Properties::envelope.property_id = g_quark_from_static_string (X_("envelope_FAKE")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for envelope_FAKE = %1\n", Properties::envelope.property_id)); } int @@ -332,6 +334,7 @@ ARDOUR::init (bool use_vst, bool try_optimization) Region::make_property_quarks (); AudioRegion::make_property_quarks (); RouteGroup::make_property_quarks (); + Playlist::make_property_quarks (); /* this is a useful ready to use PropertyChange that many things need to check. This avoids having to compose diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index 048a54da93..eec0cde0db 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -39,6 +39,7 @@ #include "i18n.h" using namespace ARDOUR; +using namespace PBD; using namespace std; MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden) diff --git a/libs/ardour/midi_ring_buffer.cc b/libs/ardour/midi_ring_buffer.cc index a050422ae5..2d5eedb6eb 100644 --- a/libs/ardour/midi_ring_buffer.cc +++ b/libs/ardour/midi_ring_buffer.cc @@ -25,6 +25,7 @@ using namespace std; using namespace ARDOUR; +using namespace PBD; /** Read a block of MIDI events from buffer into a MidiBuffer. * diff --git a/libs/ardour/midi_ui.cc b/libs/ardour/midi_ui.cc index fb85309ee1..be13209513 100644 --- a/libs/ardour/midi_ui.cc +++ b/libs/ardour/midi_ui.cc @@ -34,6 +34,7 @@ using namespace std; using namespace ARDOUR; +using namespace PBD; using namespace Glib; #include "i18n.h" diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index e128489820..1aef4b8d92 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -46,6 +46,12 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +namespace ARDOUR { +namespace Properties { +PBD::PropertyDescriptor regions; +} +} + struct ShowMeTheList { ShowMeTheList (boost::shared_ptr pl, const string& n) : playlist (pl), name (n) {} ~ShowMeTheList () { @@ -90,9 +96,66 @@ struct RegionSortByLastLayerOp { } }; +void +Playlist::make_property_quarks () +{ + Properties::regions.property_id = g_quark_from_static_string (X_("regions")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id)); +} + +RegionListProperty::RegionListProperty (Playlist& pl) + : SequenceProperty > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1)) + , _playlist (pl) +{ +} + +boost::shared_ptr +RegionListProperty::lookup_id (const ID& id) +{ + boost::shared_ptr ret = _playlist.region_by_id (id); + + if (!ret) { + ret = _playlist.session().region_by_id (id); + } + + if (!ret) { + ret = RegionFactory::region_by_id (id); + } + return ret; +} + +RegionListProperty* +RegionListProperty::copy_for_history () const +{ + RegionListProperty* copy = new RegionListProperty (_playlist); + /* this is all we need */ + copy->_change = _change; + return copy; +} + +void +RegionListProperty::diff (PropertyList& before, PropertyList& after) const +{ + if (_have_old) { + RegionListProperty* a = copy_for_history (); + RegionListProperty* b = copy_for_history (); + + b->invert_changes (); + + before.add (b); + after.add (a); + + cerr << "pdiff on " << _playlist.name() << " before contains " + << b->change().added.size() << " adds and " << b->change().removed.size() << " removes\n"; + cerr << "pdiff on " << _playlist.name() << " after contains " + << a->change().added.size() << " adds and " << a->change().removed.size() << " removes\n"; + + } +} Playlist::Playlist (Session& sess, string nom, DataType type, bool hide) : SessionObject(sess, nom) + , regions (*this) , _type(type) { init (hide); @@ -103,7 +166,9 @@ Playlist::Playlist (Session& sess, string nom, DataType type, bool hide) Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide) : SessionObject(sess, "unnamed playlist") - , _type(type) + , regions (*this) + , _type(type) + { const XMLProperty* prop = node.property("type"); assert(!prop || DataType(prop->value()) == _type); @@ -116,6 +181,7 @@ Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide Playlist::Playlist (boost::shared_ptr other, string namestr, bool hide) : SessionObject(other->_session, namestr) + , regions (*this) , _type(other->_type) , _orig_diskstream_id(other->_orig_diskstream_id) { @@ -150,6 +216,7 @@ Playlist::Playlist (boost::shared_ptr other, string namestr, boo Playlist::Playlist (boost::shared_ptr other, framepos_t start, framecnt_t cnt, string str, bool hide) : SessionObject(other->_session, str) + , regions (*this) , _type(other->_type) , _orig_diskstream_id(other->_orig_diskstream_id) { @@ -256,6 +323,9 @@ Playlist::copy_regions (RegionList& newlist) const void Playlist::init (bool hide) { + add_property (regions); + _xml_node_name = X_("Playlist"); + g_atomic_int_set (&block_notifications, 0); g_atomic_int_set (&ignore_state_changes, 0); pending_contents_change = false; @@ -362,7 +432,7 @@ Playlist::release_notifications () { if (g_atomic_int_dec_and_test (&block_notifications)) { flush_notifications (); - } + } } void @@ -811,7 +881,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re get operated on as well. */ - RegionList copy = regions; + RegionList copy = regions.rlist(); for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) { @@ -1189,7 +1259,7 @@ void Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue) { RegionLock rlock (this); - RegionList copy (regions); + RegionList copy (regions.rlist()); RegionList fixup; for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { @@ -1227,7 +1297,7 @@ void Playlist::split (framepos_t at) { RegionLock rlock (this); - RegionList copy (regions); + RegionList copy (regions.rlist()); /* use a copy since this operation can modify the region list */ @@ -1406,9 +1476,10 @@ Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shar RegionList::iterator i = find (regions.begin(), regions.end(), region); if (i == regions.end()) { - warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"), - _name, region->name()) - << endmsg; + /* the region bounds are being modified but its not currently + in the region list. we will use its bounds correctly when/if + it is added + */ return; } @@ -1939,6 +2010,63 @@ Playlist::mark_session_dirty () } } +bool +Playlist::set_property (const PropertyBase& prop) +{ + if (prop == Properties::regions.property_id) { + const RegionListProperty::ChangeRecord& change (dynamic_cast(&prop)->change()); + regions.update (change); + return (!change.added.empty() && !change.removed.empty()); + } + return false; +} + +void +Playlist::update (const RegionListProperty::ChangeRecord& change) +{ + DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n", + name(), change.added.size(), change.removed.size())); + + freeze (); + /* add the added regions */ + for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) { + add_region ((*i), (*i)->position()); + } + /* remove the removed regions */ + for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) { + remove_region (*i); + } + thaw (); +} + +PropertyList* +Playlist::property_factory (const XMLNode& history_node) const +{ + const XMLNodeList& children (history_node.children()); + PropertyList* prop_list = 0; + + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + + /* XXX property name needs capitalizing */ + + if ((*i)->name() == regions.property_name()) { + + RegionListProperty* rlp = new RegionListProperty (*const_cast (this)); + + if (rlp->load_history_state (**i)) { + if (!prop_list) { + prop_list = new PropertyList(); + } + prop_list->add (rlp); + } else { + delete rlp; + } + } + } + + return prop_list; +} + int Playlist::set_state (const XMLNode& node, int version) { @@ -1968,6 +2096,8 @@ Playlist::set_state (const XMLNode& node, int version) if (prop->name() == X_("name")) { _name = prop->value(); + } else if (prop->name() == X_("id")) { + _id = prop->value(); } else if (prop->name() == X_("orig_diskstream_id")) { _orig_diskstream_id = prop->value (); } else if (prop->name() == X_("frozen")) { @@ -2053,6 +2183,7 @@ Playlist::state (bool full_state) XMLNode *node = new XMLNode (X_("Playlist")); char buf[64]; + node->add_property (X_("id"), id().to_s()); node->add_property (X_("name"), _name); node->add_property (X_("type"), _type.to_string()); @@ -2177,7 +2308,7 @@ Playlist::relayer () which depends on the layer model */ - RegionList copy = regions; + RegionList copy = regions.rlist(); /* sort according to the model and the layering mode that we're in */ @@ -2191,6 +2322,7 @@ Playlist::relayer () } + for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) { /* reset the pending explicit relayer flag for every region, now that we're relayering */ @@ -2447,7 +2579,7 @@ Playlist::find_region (const ID& id) const } boost::shared_ptr -Playlist::region_by_id (ID id) +Playlist::region_by_id (const ID& id) { /* searches all regions ever added to this playlist */ @@ -2627,7 +2759,7 @@ void Playlist::update_after_tempo_map_change () { RegionLock rlock (const_cast (this)); - RegionList copy (regions); + RegionList copy (regions.rlist()); freeze (); diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index ca89c3cec0..f59d076c59 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -77,27 +77,48 @@ PBD::Signal2,const PropertyChange&> Regio void Region::make_property_quarks () { - Properties::muted.id = g_quark_from_static_string (X_("muted")); - Properties::opaque.id = g_quark_from_static_string (X_("opaque")); - Properties::locked.id = g_quark_from_static_string (X_("locked")); - Properties::automatic.id = g_quark_from_static_string (X_("automatic")); - Properties::whole_file.id = g_quark_from_static_string (X_("whole-file")); - Properties::import.id = g_quark_from_static_string (X_("import")); - Properties::external.id = g_quark_from_static_string (X_("external")); - Properties::sync_marked.id = g_quark_from_static_string (X_("sync-marked")); - Properties::left_of_split.id = g_quark_from_static_string (X_("left-of-split")); - Properties::right_of_split.id = g_quark_from_static_string (X_("right-of-split")); - Properties::hidden.id = g_quark_from_static_string (X_("hidden")); - Properties::position_locked.id = g_quark_from_static_string (X_("position-locked")); - Properties::start.id = g_quark_from_static_string (X_("start")); - Properties::length.id = g_quark_from_static_string (X_("length")); - Properties::position.id = g_quark_from_static_string (X_("position")); - Properties::sync_position.id = g_quark_from_static_string (X_("sync-position")); - Properties::layer.id = g_quark_from_static_string (X_("layer")); - Properties::ancestral_start.id = g_quark_from_static_string (X_("ancestral-start")); - Properties::ancestral_length.id = g_quark_from_static_string (X_("ancestral-length")); - Properties::stretch.id = g_quark_from_static_string (X_("stretch")); - Properties::shift.id = g_quark_from_static_string (X_("shift")); + Properties::muted.property_id = g_quark_from_static_string (X_("muted")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n", Properties::muted.property_id)); + Properties::opaque.property_id = g_quark_from_static_string (X_("opaque")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n", Properties::opaque.property_id)); + Properties::locked.property_id = g_quark_from_static_string (X_("locked")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n", Properties::locked.property_id)); + Properties::automatic.property_id = g_quark_from_static_string (X_("automatic")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n", Properties::automatic.property_id)); + Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n", Properties::whole_file.property_id)); + Properties::import.property_id = g_quark_from_static_string (X_("import")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n", Properties::import.property_id)); + Properties::external.property_id = g_quark_from_static_string (X_("external")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n", Properties::external.property_id)); + Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n", Properties::sync_marked.property_id)); + Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n", Properties::left_of_split.property_id)); + Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n", Properties::right_of_split.property_id)); + Properties::hidden.property_id = g_quark_from_static_string (X_("hidden")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id)); + Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n", Properties::position_locked.property_id)); + Properties::start.property_id = g_quark_from_static_string (X_("start")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n", Properties::start.property_id)); + Properties::length.property_id = g_quark_from_static_string (X_("length")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n", Properties::length.property_id)); + Properties::position.property_id = g_quark_from_static_string (X_("position")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n", Properties::position.property_id)); + Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n", Properties::sync_position.property_id)); + Properties::layer.property_id = g_quark_from_static_string (X_("layer")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n", Properties::layer.property_id)); + Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n", Properties::ancestral_start.property_id)); + Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n", Properties::ancestral_length.property_id)); + Properties::stretch.property_id = g_quark_from_static_string (X_("stretch")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n", Properties::stretch.property_id)); + Properties::shift.property_id = g_quark_from_static_string (X_("shift")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id)); } void @@ -1516,7 +1537,7 @@ Region::set_property (const PropertyBase& prop) { DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 set property %2\n", _name.val(), prop.property_name())); - if (prop == Properties::muted.id) { + if (prop == Properties::muted.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _muted) { DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 muted changed from %2 to %3", @@ -1524,7 +1545,7 @@ Region::set_property (const PropertyBase& prop) _muted = val; return true; } - } else if (prop == Properties::opaque.id) { + } else if (prop == Properties::opaque.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _opaque) { DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 opaque changed from %2 to %3", @@ -1532,7 +1553,7 @@ Region::set_property (const PropertyBase& prop) _opaque = val; return true; } - } else if (prop == Properties::locked.id) { + } else if (prop == Properties::locked.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _locked) { DEBUG_TRACE (DEBUG::Properties, string_compose ("region %1 locked changed from %2 to %3", @@ -1540,61 +1561,61 @@ Region::set_property (const PropertyBase& prop) _locked = val; return true; } - } else if (prop == Properties::automatic.id) { + } else if (prop == Properties::automatic.property_id) { _automatic = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::whole_file.id) { + } else if (prop == Properties::whole_file.property_id) { _whole_file = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::import.id) { + } else if (prop == Properties::import.property_id) { _import = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::external.id) { + } else if (prop == Properties::external.property_id) { _external = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::sync_marked.id) { + } else if (prop == Properties::sync_marked.property_id) { _sync_marked = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::left_of_split.id) { + } else if (prop == Properties::left_of_split.property_id) { _left_of_split = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::right_of_split.id) { + } else if (prop == Properties::right_of_split.property_id) { _right_of_split = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::hidden.id) { + } else if (prop == Properties::hidden.property_id) { bool val = dynamic_cast*>(&prop)->val(); if (val != _hidden) { _hidden = val; return true; } - } else if (prop == Properties::position_locked.id) { + } else if (prop == Properties::position_locked.property_id) { _position_locked = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::start.id) { + } else if (prop == Properties::start.property_id) { _start = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::length.id) { + } else if (prop == Properties::length.property_id) { framecnt_t val = dynamic_cast* > (&prop)->val(); if (val != _length) { _length = val; return true; } - } else if (prop == Properties::position.id) { + } else if (prop == Properties::position.property_id) { framepos_t val = dynamic_cast*>(&prop)->val(); if (val != _position) { _position = val; return true; } - } else if (prop == Properties::sync_position.id) { + } else if (prop == Properties::sync_position.property_id) { framepos_t val = dynamic_cast*>(&prop)->val(); if (val != _sync_position) { _sync_position = val; return true; } - } else if (prop == Properties::layer.id) { + } else if (prop == Properties::layer.property_id) { layer_t val = dynamic_cast*>(&prop)->val(); if (val != _layer) { _layer = val; return true; } - } else if (prop == Properties::ancestral_start.id) { + } else if (prop == Properties::ancestral_start.property_id) { _ancestral_start = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::ancestral_length.id) { + } else if (prop == Properties::ancestral_length.property_id) { _ancestral_length = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::stretch.id) { + } else if (prop == Properties::stretch.property_id) { _stretch = dynamic_cast*>(&prop)->val(); - } else if (prop == Properties::shift.id) { + } else if (prop == Properties::shift.property_id) { _shift = dynamic_cast*>(&prop)->val(); } else { return SessionObject::set_property (prop); @@ -1602,3 +1623,19 @@ Region::set_property (const PropertyBase& prop) return false; } + +PropertyList* +Region::property_factory (const XMLNode& history_node) const +{ + PropertyList* prop_list = new PropertyList; + + for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) { + PropertyBase* prop = i->second->maybe_clone_self_if_found_in_history_node (history_node); + + if (prop) { + prop_list->add (prop); + } + } + + return prop_list; +} diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc index 348dc72041..d2c5fa1b22 100644 --- a/libs/ardour/region_factory.cc +++ b/libs/ardour/region_factory.cc @@ -36,7 +36,8 @@ using namespace ARDOUR; using namespace PBD; PBD::Signal1 > RegionFactory::CheckNewRegion; -map > RegionFactory::region_map; +Glib::StaticMutex RegionFactory::region_map_lock; +RegionFactory::RegionMap RegionFactory::region_map; boost::shared_ptr RegionFactory::create (boost::shared_ptr region) @@ -302,23 +303,40 @@ RegionFactory::create (SourceList& srcs, const XMLNode& node) void RegionFactory::map_add (boost::shared_ptr r) { - pair > p; + pair > p; p.first = r->id(); p.second = r; - region_map.insert (p); + { + Glib::Mutex::Lock lm (region_map_lock); + region_map.insert (p); + /* we pay no attention to attempts to delete regions */ + } +} + +void +RegionFactory::map_remove (boost::shared_ptr r) +{ + { + Glib::Mutex::Lock lm (region_map_lock); + RegionMap::iterator i = region_map.find (r->id()); + if (i != region_map.end()) { + region_map.erase (i); + } + } } boost::shared_ptr RegionFactory::region_by_id (const PBD::ID& id) { - map >::iterator i = region_map.find (id); + RegionMap::iterator i = region_map.find (id); if (i == region_map.end()) { + cerr << "ID " << id << " not found in region map\n"; return boost::shared_ptr(); } - return i->second.lock(); + return i->second; } void diff --git a/libs/ardour/route_group.cc b/libs/ardour/route_group.cc index f31820e924..3e9ffe16d6 100644 --- a/libs/ardour/route_group.cc +++ b/libs/ardour/route_group.cc @@ -28,6 +28,7 @@ #include "pbd/strsplit.h" #include "ardour/amp.h" +#include "ardour/debug.h" #include "ardour/route_group.h" #include "ardour/audio_track.h" #include "ardour/audio_diskstream.h" @@ -56,15 +57,24 @@ namespace ARDOUR { void RouteGroup::make_property_quarks () { - Properties::relative.id = g_quark_from_static_string (X_("relative")); - Properties::active.id = g_quark_from_static_string (X_("active")); - Properties::hidden.id = g_quark_from_static_string (X_("hidden")); - Properties::gain.id = g_quark_from_static_string (X_("gain")); - Properties::mute.id = g_quark_from_static_string (X_("mute")); - Properties::solo.id = g_quark_from_static_string (X_("solo")); - Properties::recenable.id = g_quark_from_static_string (X_("recenable")); - Properties::select.id = g_quark_from_static_string (X_("select")); - Properties::edit.id = g_quark_from_static_string (X_("edit")); + Properties::relative.property_id = g_quark_from_static_string (X_("relative")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n", Properties::relative.property_id)); + Properties::active.property_id = g_quark_from_static_string (X_("active")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n", Properties::active.property_id)); + Properties::hidden.property_id = g_quark_from_static_string (X_("hidden")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id)); + Properties::gain.property_id = g_quark_from_static_string (X_("gain")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n", Properties::gain.property_id)); + Properties::mute.property_id = g_quark_from_static_string (X_("mute")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n", Properties::mute.property_id)); + Properties::solo.property_id = g_quark_from_static_string (X_("solo")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n", Properties::solo.property_id)); + Properties::recenable.property_id = g_quark_from_static_string (X_("recenable")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n", Properties::recenable.property_id)); + Properties::select.property_id = g_quark_from_static_string (X_("select")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n", Properties::select.property_id)); + Properties::edit.property_id = g_quark_from_static_string (X_("edit")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for edit = %1\n", Properties::edit.property_id)); } #define ROUTE_GROUP_DEFAULT_PROPERTIES _relative (Properties::relative, false) \ @@ -432,23 +442,23 @@ RouteGroup::destroy_subgroup () bool RouteGroup::enabled_property (PBD::PropertyID prop) { - if (Properties::relative.id == prop) { + if (Properties::relative.property_id == prop) { return is_relative(); - } else if (Properties::active.id == prop) { + } else if (Properties::active.property_id == prop) { return is_active(); - } else if (Properties::hidden.id == prop) { + } else if (Properties::hidden.property_id == prop) { return is_hidden(); - } else if (Properties::gain.id == prop) { + } else if (Properties::gain.property_id == prop) { return is_gain(); - } else if (Properties::mute.id == prop) { + } else if (Properties::mute.property_id == prop) { return is_mute(); - } else if (Properties::solo.id == prop) { + } else if (Properties::solo.property_id == prop) { return is_solo(); - } else if (Properties::recenable.id == prop) { + } else if (Properties::recenable.property_id == prop) { return is_recenable(); - } else if (Properties::select.id == prop) { + } else if (Properties::select.property_id == prop) { return is_select(); - } else if (Properties::edit.id == prop) { + } else if (Properties::edit.property_id == prop) { return is_edit(); } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index c549244e64..0c2f12d91d 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2803,9 +2803,9 @@ Session::remove_region (boost::weak_ptr weak_region) } boost::shared_ptr -Session::find_whole_file_parent (boost::shared_ptr child) +Session::find_whole_file_parent (boost::shared_ptr child) const { - RegionList::iterator i; + RegionList::const_iterator i; boost::shared_ptr region; Glib::Mutex::Lock lm (region_lock); @@ -2825,6 +2825,20 @@ Session::find_whole_file_parent (boost::shared_ptr child) return boost::shared_ptr (); } +boost::shared_ptr +Session::region_by_id (const PBD::ID& id) const +{ + Glib::Mutex::Lock lm (region_lock); + + RegionList::const_iterator i = regions.find (id); + + if (i != regions.end()) { + return i->second; + } + + return boost::shared_ptr (); +} + int Session::destroy_region (boost::shared_ptr region) { diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc index 6c2621fdb9..3ff68a06fb 100644 --- a/libs/ardour/session_command.cc +++ b/libs/ardour/session_command.cc @@ -146,7 +146,15 @@ Session::stateful_diff_command_factory (XMLNode* n) if (r) { return new StatefulDiffCommand (r, *n); } - } + + } else if (obj_T == typeid (AudioPlaylist).name() || obj_T == typeid (MidiPlaylist).name()) { + boost::shared_ptr p = playlists->by_id (id); + if (p) { + return new StatefulDiffCommand (p, *n); + } else { + cerr << "Playlist with ID = " << id << " not found\n"; + } + } /* we failed */ diff --git a/libs/ardour/session_object.cc b/libs/ardour/session_object.cc index b245b43ae5..3e71b6a73e 100644 --- a/libs/ardour/session_object.cc +++ b/libs/ardour/session_object.cc @@ -34,13 +34,14 @@ namespace ARDOUR { void SessionObject::make_property_quarks () { - Properties::name.id = g_quark_from_static_string (X_("name")); + Properties::name.property_id = g_quark_from_static_string (X_("name")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for name = %1\n", Properties::name.property_id)); } bool SessionObject::set_property (const PropertyBase& prop) { - if (prop == Properties::name.id) { + if (prop == Properties::name.property_id) { std::string str = dynamic_cast*>(&prop)->val(); if (_name != str) { DEBUG_TRACE (DEBUG::Properties, string_compose ("session object named %1 renamed %2\n", diff --git a/libs/ardour/session_playlists.cc b/libs/ardour/session_playlists.cc index 17ed6a4e30..2f8ae9a8f0 100644 --- a/libs/ardour/session_playlists.cc +++ b/libs/ardour/session_playlists.cc @@ -167,6 +167,26 @@ SessionPlaylists::by_name (string name) return boost::shared_ptr(); } +boost::shared_ptr +SessionPlaylists::by_id (const PBD::ID& id) +{ + Glib::Mutex::Lock lm (lock); + + for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) { + if ((*i)->id() == id) { + return* i; + } + } + + for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + if ((*i)->id() == id) { + return* i; + } + } + + return boost::shared_ptr(); +} + void SessionPlaylists::unassigned (std::list > & list) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 434410b39e..014200da1c 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -991,7 +991,7 @@ Session::state(bool full_state) if (full_state) { Glib::Mutex::Lock rl (region_lock); - +#if 0 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { /* only store regions not attached to playlists */ @@ -1000,6 +1000,17 @@ Session::state(bool full_state) child->add_child_nocopy (i->second->state (true)); } } +#else + const RegionFactory::RegionMap& region_map (RegionFactory::all_regions()); + for (RegionFactory::RegionMap::const_iterator i = region_map.begin(); i != region_map.end(); ++i) { + boost::shared_ptr r = i->second; + /* only store regions not attached to playlists */ + if (r->playlist() == 0) { + child->add_child_nocopy (r->state (true)); + } + } +#endif + } child = node->add_child ("DiskStreams"); diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 239c3729c9..5ec9631e40 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -45,6 +45,7 @@ using namespace std; using namespace ARDOUR; +using namespace PBD; Source::Source (Session& s, DataType type, const string& name, Flag flags) : SessionObject(s, name) diff --git a/libs/pbd/debug.cc b/libs/pbd/debug.cc new file mode 100644 index 0000000000..f8abdcb488 --- /dev/null +++ b/libs/pbd/debug.cc @@ -0,0 +1,108 @@ +/* + Copyright (C) 2009 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include + +#include "pbd/debug.h" + +#include "i18n.h" + +using namespace std; +static uint64_t _debug_bit = 1; +static std::map _debug_bit_map; + +uint64_t PBD::DEBUG::Stateful = PBD::new_debug_bit ("stateful"); +uint64_t PBD::DEBUG::Properties = PBD::new_debug_bit ("properties"); + +uint64_t PBD::debug_bits = 0x0; + +uint64_t +PBD::new_debug_bit (const char* name) +{ + uint64_t ret; + _debug_bit_map.insert (make_pair (name, _debug_bit)); + cerr << "debug name " << name << " = " << _debug_bit << endl; + ret = _debug_bit; + _debug_bit <<= 1; + return ret; +} + +void +PBD::debug_print (const char* prefix, string str) +{ + cerr << prefix << ": " << str; +} + +void +PBD::set_debug_bits (uint64_t bits) +{ + debug_bits = bits; +} + +int +PBD::parse_debug_options (const char* str) +{ + char* p; + char* sp; + uint64_t bits = 0; + char* copy = strdup (str); + + p = strtok_r (copy, ",", &sp); + + while (p) { + if (strcasecmp (p, "list") == 0) { + list_debug_options (); + free (copy); + return 1; + } + + if (strcasecmp (p, "all") == 0) { + PBD::set_debug_bits (~0ULL); + free (copy); + return 0; + } + + for (map::iterator i = _debug_bit_map.begin(); i != _debug_bit_map.end(); ++i) { + if (strncasecmp (p, i->first, strlen (p)) == 0) { + cerr << "debug args matched for " << p << " set bit " << i->second << endl; + bits |= i->second; + } + } + + p = strtok_r (0, ",", &sp); + } + + free (copy); + PBD::set_debug_bits (bits); + return 0; +} + +void +PBD::list_debug_options () +{ + cerr << _("The following debug options are available. Separate multipe options with commas.\nNames are case-insensitive and can be abbreviated.") << endl << endl; + cerr << "\tAll" << endl; + + for (map::iterator i = _debug_bit_map.begin(); i != _debug_bit_map.end(); ++i) { + cerr << "\t" << i->first << endl; + } +} diff --git a/libs/pbd/pbd/debug.h b/libs/pbd/pbd/debug.h new file mode 100644 index 0000000000..a45a867bd2 --- /dev/null +++ b/libs/pbd/pbd/debug.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 2009 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_debug_h__ +#define __libpbd_debug_h__ + +#include + +#include + +namespace PBD { + + extern uint64_t debug_bits; + uint64_t new_debug_bit (const char* name); + void debug_print (const char* prefix, std::string str); + void set_debug_bits (uint64_t bits); + int parse_debug_options (const char* str); + void list_debug_options (); + + namespace DEBUG { + + /* this namespace is so that we can write DEBUG::bit_name */ + + extern uint64_t Stateful; + extern uint64_t Properties; + } +} + +#ifndef NDEBUG +#define DEBUG_TRACE(bits,str) if ((bits) & PBD::debug_bits) { PBD::debug_print (# bits, str); } +#define DEBUG_STR_DECL(id) std::stringstream __debug_str ## id; +#define DEBUG_STR(id) __debug_str ## id +#define DEBUG_STR_APPEND(id,s) __debug_str ## id << s; +#else +#define DEBUG_TRACE(bits,fmt,...) /*empty*/ +#define DEBUG_STR(a) /* empty */ +#define DEBUG_STR_APPEND(a,b) /* empty */ +#endif + +#endif /* __libpbd_debug_h__ */ + diff --git a/libs/pbd/pbd/properties.h b/libs/pbd/pbd/properties.h index 1c3519766f..274dfffd9c 100644 --- a/libs/pbd/pbd/properties.h +++ b/libs/pbd/pbd/properties.h @@ -27,96 +27,19 @@ #include #include "pbd/xml++.h" +#include "pbd/property_basics.h" +#include "pbd/property_list.h" namespace PBD { -typedef GQuark PropertyID; - -template -struct PropertyDescriptor { - PropertyID id; - typedef T value_type; -}; - -class PropertyChange : public std::set -{ -public: - PropertyChange() {} - - template - PropertyChange(PropertyDescriptor p) { insert (p.id); } - - PropertyChange(const PropertyChange& other) : std::set (other) {} - - PropertyChange operator=(const PropertyChange& other) { - clear (); - insert (other.begin (), other.end ()); - return *this; - } - - template - PropertyChange operator=(PropertyDescriptor p) { - clear (); - insert (p.id); - return *this; - } - - template - bool contains (PropertyDescriptor p) const { return find (p.id) != end (); } - - bool contains (const PropertyChange& other) const { - for (const_iterator x = other.begin (); x != other.end (); ++x) { - if (find (*x) != end ()) { - return true; - } - } - return false; - } - - void add (PropertyID id) { (void) insert (id); } - void add (const PropertyChange& other) { (void) insert (other.begin (), other.end ()); } - template - void add (PropertyDescriptor p) { (void)insert (p.id); } -}; - -/** Base (non template) part of Property */ -class PropertyBase -{ -public: - PropertyBase (PropertyID pid) - : _property_id (pid) - , _have_old (false) - {} - - /** Forget about any old value for this state */ - void clear_history () { - _have_old = false; - } - - virtual void diff (XMLNode*, XMLNode*) const = 0; - virtual void diff (PropertyChange&) const = 0; - virtual bool set_state (XMLNode const&) = 0; - virtual void add_state (XMLNode&) const = 0; - - const gchar*property_name () const { return g_quark_to_string (_property_id); } - PropertyID id () const { return _property_id; } - - bool operator==(PropertyID pid) const { - return _property_id == pid; - } - -protected: - PropertyID _property_id; - bool _have_old; -}; - -/** Parent class for classes which represent a single property in a Stateful object */ +/** Parent class for classes which represent a single scalar property in a Stateful object + */ template class PropertyTemplate : public PropertyBase { public: PropertyTemplate (PropertyDescriptor p, T const& v) - : PropertyBase (p.id) + : PropertyBase (p.property_id) , _current (v) {} @@ -156,25 +79,22 @@ public: return _current; } - void diff (XMLNode* old, XMLNode* current) const { - if (_have_old) { - old->add_property (property_name (), to_string (_old)); - current->add_property (property_name (), to_string (_current)); - } - } - - void diff (PropertyChange& c) const { - if (_have_old) { - c.add (_property_id); - } + /** If this property has been changed since the last clear_history() call + (or its construction), add an (XML) property describing the old value + to the XMLNode @param old and another describing the current value to + the XMLNode @param current. + */ + void add_history_state (XMLNode* history_node) const { + history_node->add_property (property_name(), to_string (_current)); } /** Try to set state from the property of an XML node. * @param node XML node. * @return true if the value of the property is changed */ - bool set_state (XMLNode const& node) { - XMLProperty const* p = node.property (property_name ()); + bool set_state_from_owner_state (XMLNode const& owner_state) { + + XMLProperty const* p = owner_state.property (property_name()); if (p) { T const v = from_string (p->value ()); @@ -188,11 +108,19 @@ public: return false; } - void add_state (XMLNode& node) const { - node.add_property (property_name (), to_string (_current)); + void add_state_to_owner_state (XMLNode& owner_state) const { + owner_state.add_property (property_name(), to_string (_current)); } protected: + /** Constructs a PropertyTemplate with a default + value for _old and _current. + */ + + PropertyTemplate (PropertyDescriptor p) + : PropertyBase (p.property_id) + {} + void set (T const& v) { _old = _current; _have_old = true; @@ -212,7 +140,7 @@ std::ostream & operator<<(std::ostream& os, PropertyTemplate const& s) return os << s.val (); } -/** Representation of a single piece of state in a Stateful; for use +/** Representation of a single piece of scalar state in a Stateful; for use * with types that can be written to / read from stringstreams. */ template @@ -222,6 +150,21 @@ public: Property (PropertyDescriptor q, T const& v) : PropertyTemplate (q, v) {} + + void diff (PropertyList& before, PropertyList& after) const { + if (this->_have_old) { + before.add (new Property (this->property_id(), this->_old)); + after.add (new Property (this->property_id(), this->_current)); + } + } + + Property* maybe_clone_self_if_found_in_history_node (const XMLNode& node) const { + const XMLProperty* prop = node.property (this->property_name()); + if (!prop) { + return 0; + } + return new Property (this->property_id(), from_string (prop->value())); + } T & operator=(T const& v) { this->set (v); @@ -229,6 +172,12 @@ public: } private: + friend class PropertyFactory; + + Property (PropertyDescriptor q) + : PropertyTemplate (q) + {} + /* Note that we do not set a locale for the streams used * in to_string() or from_string(), because we want the * format to be portable across locales (i.e. C or @@ -254,7 +203,7 @@ private: /** Specialization, for std::string which is common and special (see to_string() and from_string() * Using stringstream to read from a std::string is easy to get wrong because of whitespace - * delineation, etc. + * separators, etc. */ template<> class Property : public PropertyTemplate @@ -264,6 +213,13 @@ public: : PropertyTemplate (q, v) {} + void diff (PropertyList& before, PropertyList& after) const { + if (this->_have_old) { + before.add (new Property (PropertyDescriptor (this->property_id()), this->_old)); + after.add (new Property (PropertyDescriptor (this->property_id()), this->_current)); + } + } + std::string & operator=(std::string const& v) { this->set (v); return this->_current; @@ -280,48 +236,9 @@ private: }; -class PropertyList : public std::map -{ -public: - PropertyList() : _property_owner (true) {} - virtual ~PropertyList() { - if (_property_owner) { - for (std::map::iterator i = begin (); i != end (); ++i) { - delete i->second; - } - } - } - - /* Classes that own property lists use this to add their - * property members to their plists. - */ - bool add (PropertyBase& p) { - return insert (value_type (p.id (), &p)).second; - } - - /* Code that is constructing a property list for use - * in setting the state of an object uses this. - */ - template - bool add (PropertyDescriptor pid, const V& v) { - return insert (value_type (pid.id, new Property (pid, (T)v))).second; - } - -protected: - bool _property_owner; -}; - -/** A variant of PropertyList that does not delete its - * property list in its destructor. Objects with their - * own Properties store them in an OwnedPropertyList - * to avoid having them deleted at the wrong time. - */ -class OwnedPropertyList : public PropertyList -{ -public: - OwnedPropertyList() { _property_owner = false; } -}; - } /* namespace PBD */ +#include "pbd/property_list_impl.h" +#include "pbd/property_basics_impl.h" + #endif /* __pbd_properties_h__ */ diff --git a/libs/pbd/pbd/property_basics.h b/libs/pbd/pbd/property_basics.h new file mode 100644 index 0000000000..c99fee1961 --- /dev/null +++ b/libs/pbd/pbd/property_basics.h @@ -0,0 +1,116 @@ +/* + Copyright (C) 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_property_basics_h__ +#define __libpbd_property_basics_h__ + +#include +#include + +#include "pbd/xml++.h" + +namespace PBD { + +class PropertyList; +typedef GQuark PropertyID; + +template +struct PropertyDescriptor { + PropertyDescriptor () : property_id (0) {} + PropertyDescriptor (PropertyID pid) : property_id (pid) {} + + PropertyID property_id; + typedef T value_type; +}; + +class PropertyChange : public std::set +{ +public: + PropertyChange() {} + + template PropertyChange(PropertyDescriptor p); + + PropertyChange(const PropertyChange& other) : std::set (other) {} + + PropertyChange operator=(const PropertyChange& other) { + clear (); + insert (other.begin (), other.end ()); + return *this; + } + + template PropertyChange operator=(PropertyDescriptor p); + template bool contains (PropertyDescriptor p) const; + + bool contains (const PropertyChange& other) const { + for (const_iterator x = other.begin (); x != other.end (); ++x) { + if (find (*x) != end ()) { + return true; + } + } + return false; + } + + void add (PropertyID id) { insert (id); } + void add (const PropertyChange& other) { insert (other.begin (), other.end ()); } + template void add (PropertyDescriptor p); +}; + +/** Base (non template) part of Property */ +class PropertyBase +{ +public: + PropertyBase (PropertyID pid) + : _property_id (pid) + , _have_old (false) + {} + + /** Forget about any old value for this state */ + virtual void clear_history () { + _have_old = false; + } + + virtual void add_history_state (XMLNode*) const = 0; + virtual void diff (PropertyList&, PropertyList&) const = 0; + + virtual PropertyBase* maybe_clone_self_if_found_in_history_node (const XMLNode&) const { return 0; } + + virtual bool set_state_from_owner_state (XMLNode const&) = 0; + virtual void add_state_to_owner_state (XMLNode&) const = 0; + + const gchar*property_name () const { return g_quark_to_string (_property_id); } + PropertyID property_id () const { return _property_id; } + + bool operator==(PropertyID pid) const { + return _property_id == pid; + } + +protected: + PropertyID _property_id; + bool _have_old; +}; + +class PropertyFactory +{ + public: + static PropertyBase* create (const XMLNode&); +}; + +} + +#endif /* __libpbd_property_basics_h__ */ diff --git a/libs/pbd/pbd/property_basics_impl.h b/libs/pbd/pbd/property_basics_impl.h new file mode 100644 index 0000000000..c63c5ccc5c --- /dev/null +++ b/libs/pbd/pbd/property_basics_impl.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_property_basics_impl_h__ +#define __libpbd_property_basics_impl_h__ + +namespace PBD { + +template +PropertyChange::PropertyChange(PropertyDescriptor p) +{ + insert (p.property_id); +} + +template PropertyChange +PropertyChange::operator=(PropertyDescriptor p) +{ + clear (); + insert (p.property_id); + return *this; +} + +template bool +PropertyChange::contains (PropertyDescriptor p) const +{ + return find (p.property_id) != end (); +} + +template void +PropertyChange::add (PropertyDescriptor p) +{ + insert (p.property_id); +} + +} + +#endif /* __libpbd_property_basics_impl_h__ */ diff --git a/libs/pbd/pbd/property_list.h b/libs/pbd/pbd/property_list.h new file mode 100644 index 0000000000..af9d10b88f --- /dev/null +++ b/libs/pbd/pbd/property_list.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __pbd_property_list_h__ +#define __pbd_property_list_h__ + +#include + +#include "pbd/property_basics.h" + +class XMLNode; + +namespace PBD { +class PropertyList : public std::map +{ +public: + PropertyList(); + + virtual ~PropertyList(); + + void add_history_state (XMLNode* before); + + /** Add a property (of some kind) to the list. Used when + constructing PropertyList's that describe a change/operation. + */ + bool add (PropertyBase* prop); + + /* Code that is constructing a property list for use + * in setting the state of an object uses this. + * + * Defined below, once we have Property + */ + template bool add (PropertyDescriptor pid, const V& v); + +protected: + bool _property_owner; +}; + +/** A variant of PropertyList that does not delete its + * property list in its destructor. Objects with their + * own Properties store them in an OwnedPropertyList + * to avoid having them deleted at the wrong time. + */ +class OwnedPropertyList : public PropertyList +{ +public: + OwnedPropertyList(); + + /* Classes that own property lists use this to add their + * property members to their plists. Note that it takes + * a reference argument rather than a pointer like + * one of the add() methods in PropertyList. + */ + bool add (PropertyBase& p); +}; + +} + +#endif /* __pbd_property_list_h__ */ diff --git a/libs/pbd/pbd/property_list_impl.h b/libs/pbd/pbd/property_list_impl.h new file mode 100644 index 0000000000..c5cfae11dc --- /dev/null +++ b/libs/pbd/pbd/property_list_impl.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libpbd_property_list_impl_h__ +#define __libpbd_property_list_impl_h__ + +#include "pbd/property_list.h" +#include "pbd/properties.h" + +/* now we can define this ... */ + +namespace PBD { + +template bool +PropertyList::add (PropertyDescriptor pid, const V& v) { + return insert (value_type (pid.property_id, new Property (pid, (T)v))).second; +} + +} + +#endif /* __libpbd_property_list_impl_h__ */ diff --git a/libs/pbd/pbd/sequence_property.h b/libs/pbd/pbd/sequence_property.h new file mode 100644 index 0000000000..887250ab82 --- /dev/null +++ b/libs/pbd/pbd/sequence_property.h @@ -0,0 +1,291 @@ +#ifndef __libpbd_sequence_property_h__ +#define __libpbd_sequence_property_h__ + +#include + +#include +#include + +#include + +#include "pbd/id.h" +#include "pbd/property_basics.h" + +#include "i18n.h" + +namespace PBD { +template +class SequenceProperty : public PropertyBase +{ + public: + typedef std::set ChangeContainer; + + struct ChangeRecord { + ChangeContainer added; + ChangeContainer removed; + }; + + SequenceProperty (PropertyID id, const boost::function& update) + : PropertyBase (id), _update_callback (update) {} + + virtual typename Container::value_type lookup_id (const PBD::ID&) = 0; + + void invert_changes () { + + /* reverse the adds/removes so that this property's change member + correctly describes how to undo the changes it currently + reflects. A derived instance of this type of property will + create a pdiff() pair by copying the property twice, and + calling this method on the "before" item of the pair. + */ + + _change.removed.swap (_change.added); + } + + void add_history_state (XMLNode* history_node) const { + + /* XXX need to capitalize property name */ + XMLNode* child = new XMLNode (property_name()); + history_node->add_child_nocopy (*child); + + /* record the change described in our change member */ + + if (!_change.added.empty()) { + for (typename ChangeContainer::iterator i = _change.added.begin(); i != _change.added.end(); ++i) { + XMLNode* add_node = new XMLNode (X_("Add")); + child->add_child_nocopy (*add_node); + add_node->add_property (X_("id"), (*i)->id().to_s()); + } + } + if (!_change.removed.empty()) { + for (typename ChangeContainer::iterator i = _change.removed.begin(); i != _change.removed.end(); ++i) { + XMLNode* remove_node = new XMLNode (X_("Remove")); + child->add_child_nocopy (*remove_node); + remove_node->add_property (X_("id"), (*i)->id().to_s()); + } + } + } + + bool set_state_from_owner_state (XMLNode const& owner_state) { + + XMLProperty const* n = owner_state.property (X_("name")); + + if (!n) { + return false; + } + + assert (g_quark_from_string (n->value().c_str()) == property_id()); + + const XMLNodeList& children = owner_state.children(); + + for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) { + + if ((*c)->name() == X_("Added")) { + const XMLNodeList& grandchildren = (*c)->children(); + for (XMLNodeList::const_iterator gc = grandchildren.begin(); gc != grandchildren.end(); ++gc) { + const XMLProperty* prop = (*gc)->property (X_("id")); + if (prop) { + typename Container::value_type v = lookup_id (PBD::ID (prop->value())); + if (v) { + _change.added.insert (v); + } + } + } + } else if ((*c)->name() == X_("Removed")) { + const XMLNodeList& grandchildren = (*c)->children(); + for (XMLNodeList::const_iterator gc = grandchildren.begin(); gc != grandchildren.end(); ++gc) { + const XMLProperty* prop = (*gc)->property (X_("id")); + if (prop) { + typename Container::value_type v = lookup_id (PBD::ID (prop->value())); + if (v) { + _change.removed.insert (v); + } + } + } + } + } + + return true; + } + + void add_state_to_owner_state (XMLNode& owner_state_node) const { + for (typename Container::const_iterator i = _val.begin(); i != _val.end(); ++i) { + owner_state_node.add_child_nocopy ((*i)->get_state ()); + } + } + + void clear_history () { + PropertyBase::clear_history(); + _change.added.clear (); + _change.removed.clear (); + } + + /** Given a record of changes to this property, pass it to a callback that will + * update the property in some appropriate way. + * + * This exists because simply using std::sequence methods to add/remove items + * from the property is far too simplistic - the semantics of add/remove may + * be much more complex than that. + */ + void update (const ChangeRecord& cr) { + _update_callback (cr); + } + + /* Wrap salient methods of Sequence + */ + + typename Container::iterator begin() { return _val.begin(); } + typename Container::iterator end() { return _val.end(); } + typename Container::const_iterator begin() const { return _val.begin(); } + typename Container::const_iterator end() const { return _val.end(); } + + typename Container::reverse_iterator rbegin() { return _val.rbegin(); } + typename Container::reverse_iterator rend() { return _val.rend(); } + typename Container::const_reverse_iterator rbegin() const { return _val.rbegin(); } + typename Container::const_reverse_iterator rend() const { return _val.rend(); } + + typename Container::iterator insert (typename Container::iterator i, const typename Container::value_type& v) { + _have_old = true; + _change.added.insert (v); + return _val.insert (i, v); + } + + typename Container::iterator erase (typename Container::iterator i) { + if (i != _val.end()) { + _have_old = true; + _change.removed.insert (*i); + } + return _val.erase (i); + } + + typename Container::iterator erase (typename Container::iterator f, typename Container::iterator l) { + _have_old = true; + for (typename Container::const_iterator i = f; i != l; ++i) { + _change.removed.insert(*i); + } + return _val.erase (f, l); + } + + void push_back (const typename Container::value_type& v) { + _have_old = true; + _change.added.insert (v); + _val.push_back (v); + } + + void push_front (const typename Container::value_type& v) { + _have_old = true; + _change.added.insert (v); + _val.push_front (v); + } + + void pop_front () { + if (!_val.empty()) { + _have_old = true; + _change.removed.insert (front()); + } + _val.pop_front (); + } + + void pop_back () { + if (!_val.empty()) { + _have_old = true; + _change.removed.insert (front()); + } + _val.pop_back (); + } + + void clear () { + _have_old = true; + _change.removed.insert (_val.begin(), _val.end()); + _val.clear (); + } + + typename Container::size_type size() const { + return _val.size(); + } + + bool empty() const { + return _val.empty(); + } + + Container& operator= (const Container& other) { + _have_old = true; + _change.removed.insert (_val.begin(), _val.end()); + _change.added.insert (other.begin(), other.end()); + return _val = other; + } + + typename Container::reference front() { + return _val.front (); + } + + typename Container::const_reference front() const { + return _val.front (); + } + + typename Container::reference back() { + return _val.back (); + } + + typename Container::const_reference back() const { + return _val.back (); + } + + void sort() { + _val.sort (); + } + + template void sort(BinaryPredicate comp) { + _val.sort (comp); + } + + const ChangeRecord& change() const { return _change; } + + /* for use in building up a SequenceProperty from a serialized + version on disk. + */ + + void record_addition (typename Container::value_type v) { + _change.added.insert (v); + } + void record_removal (typename Container::value_type v) { + _change.added.erase (v); + } + + protected: + Container _val; + ChangeRecord _change; + boost::function _update_callback; + + /** Load serialized change history. + * @return true if loading succeeded, false otherwise + */ + + bool load_history_state (const XMLNode& history_node) { + + const XMLNodeList& children (history_node.children()); + + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + const XMLProperty* prop = (*i)->property ("id"); + if (prop) { + PBD::ID id (prop->value()); + typename Container::value_type v = lookup_id (id); + if (!v) { + std::cerr << "No such item, ID = " << id.to_s() << " (from " << prop->value() << ")\n"; + return false; + } + if ((*i)->name() == "Add") { + _change.added.insert (v); + } else if ((*i)->name() == "Remove") { + _change.removed.insert (v); + } + } + } + + return true; + } +}; + +} + +#endif /* __libpbd_sequence_property_h__ */ diff --git a/libs/pbd/pbd/stateful.h b/libs/pbd/pbd/stateful.h index bc32d7fd9b..78b9eb7b3b 100644 --- a/libs/pbd/pbd/stateful.h +++ b/libs/pbd/pbd/stateful.h @@ -26,7 +26,7 @@ #include "pbd/id.h" #include "pbd/xml++.h" -#include "pbd/properties.h" +#include "pbd/property_basics.h" #include "pbd/signals.h" class XMLNode; @@ -37,6 +37,9 @@ namespace sys { class path; } +class PropertyList; +class OwnedPropertyList; + /** Base class for objects with saveable and undoable state */ class Stateful { public: @@ -51,10 +54,9 @@ class Stateful { virtual bool set_property (const PropertyBase&) { return false; } PropertyChange set_properties (const PropertyList&); + const OwnedPropertyList& properties() const { return *_properties; } - void add_property (PropertyBase& s) { - _properties.add (s); - } + void add_property (PropertyBase& s); /* Extra XML node: so that 3rd parties can attach state to the XMLNode representing the state of this object. @@ -65,9 +67,13 @@ class Stateful { const PBD::ID& id() const { return _id; } + /* history management */ + void clear_history (); - std::pair diff () const; - void changed (PropertyChange&) const; + void diff (PropertyList&, PropertyList&) const; + /* create a property list from an XMLNode + */ + virtual PropertyList* property_factory(const XMLNode&) const { return 0; } /* How stateful's notify of changes to their properties */ @@ -85,7 +91,6 @@ class Stateful { to get basic property setting done. */ PropertyChange set_properties (XMLNode const &); - /* derived classes can implement this to do cross-checking of property values after either a PropertyList or XML @@ -98,7 +103,7 @@ class Stateful { PBD::ID _id; std::string _xml_node_name; ///< name of node to use for this object in XML - OwnedPropertyList _properties; + OwnedPropertyList* _properties; }; } // namespace PBD diff --git a/libs/pbd/pbd/stateful_diff_command.h b/libs/pbd/pbd/stateful_diff_command.h index 43efed445d..cab1ae40cf 100644 --- a/libs/pbd/pbd/stateful_diff_command.h +++ b/libs/pbd/pbd/stateful_diff_command.h @@ -28,6 +28,7 @@ namespace PBD { class Stateful; +class PropertyList; /** A Command which stores its action as the differences between the before and after * state of a Stateful object. @@ -46,8 +47,8 @@ public: private: boost::weak_ptr _object; ///< the object in question - XMLNode* _before; ///< XML node containing the previous values of XML properties which changed - XMLNode* _after; ///< XML node containing the new values of XML properties which changed + PBD::PropertyList* _before; ///< its (partial) state before the command + PBD::PropertyList* _after; ///< its (partial) state after the operation }; }; diff --git a/libs/pbd/property_factory.cc b/libs/pbd/property_factory.cc new file mode 100644 index 0000000000..af0a91a0bf --- /dev/null +++ b/libs/pbd/property_factory.cc @@ -0,0 +1,53 @@ +/* + Copyright (C) 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "pbd/properties.h" +#include "pbd/xml++.h" + +#include "i18n.h" + +using namespace PBD; + +PropertyBase* +PropertyFactory::create (const XMLNode& node) +{ + const XMLProperty* prop_type = node.property (X_("property-type")); + const XMLProperty* prop_id = node.property (X_("id")); + const XMLProperty* prop_val = node.property (X_("val")); + + if (!prop_type || !prop_id || !prop_val) { + return 0; + } + + PropertyID id; + sscanf (prop_id->value().c_str(), "%u", &id); + + if (prop_type->value() == typeid (Property).name()) { + + PropertyDescriptor pd (id); + Property* p = new Property (pd); + p->set (p->from_string (prop_val->value())); + return p; + } + + return 0; +} diff --git a/libs/pbd/property_list.cc b/libs/pbd/property_list.cc new file mode 100644 index 0000000000..ffe1170dd5 --- /dev/null +++ b/libs/pbd/property_list.cc @@ -0,0 +1,69 @@ +/* + Copyright (C) 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "pbd/debug.h" +#include "pbd/compose.h" +#include "pbd/property_list.h" +#include "pbd/xml++.h" + +using namespace PBD; + +PropertyList::PropertyList() + : _property_owner (true) +{ +} + +PropertyList::~PropertyList () +{ + if (_property_owner) { + for (iterator i = begin (); i != end (); ++i) { + delete i->second; + } + } +} + +void +PropertyList::add_history_state (XMLNode* history_node) +{ + for (const_iterator i = begin(); i != end(); ++i) { + DEBUG_TRACE (DEBUG::Properties, string_compose ("Add before/after to %1 for %2\n", + history_node->name(), + i->second->property_name())); + i->second->add_history_state (history_node); + } +} + +bool +PropertyList::add (PropertyBase* prop) +{ + return insert (value_type (prop->property_id(), prop)).second; +} + +OwnedPropertyList::OwnedPropertyList () +{ + _property_owner = false; +} + +bool +OwnedPropertyList::add (PropertyBase& p) +{ + return insert (value_type (p.property_id (), &p)).second; +} + + diff --git a/libs/pbd/stateful.cc b/libs/pbd/stateful.cc index 9a1a116128..f2d9f9ee12 100644 --- a/libs/pbd/stateful.cc +++ b/libs/pbd/stateful.cc @@ -20,7 +20,10 @@ #include +#include "pbd/debug.h" #include "pbd/stateful.h" +#include "pbd/property_list.h" +#include "pbd/properties.h" #include "pbd/destructible.h" #include "pbd/filesystem.h" #include "pbd/xml++.h" @@ -36,6 +39,7 @@ int Stateful::current_state_version = 0; int Stateful::loading_state_version = 0; Stateful::Stateful () + : _properties (new OwnedPropertyList) { _extra_xml = 0; _instant_xml = 0; @@ -43,6 +47,8 @@ Stateful::Stateful () Stateful::~Stateful () { + delete _properties; + // Do not delete _extra_xml. The use of add_child_nocopy() // means it needs to live on indefinately. @@ -153,53 +159,30 @@ Stateful::instant_xml (const string& str, const sys::path& directory_path) void Stateful::clear_history () { - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { + for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) { i->second->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 -Stateful::diff () const +void +Stateful::diff (PropertyList& before, PropertyList& after) const { - XMLNode* old = new XMLNode (_xml_node_name); - XMLNode* current = new XMLNode (_xml_node_name); - - for (OwnedPropertyList::const_iterator i = _properties.begin(); i != _properties.end(); ++i) { - i->second->diff (old, current); + for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) { + i->second->diff (before, after); } - - return make_pair (old, current); } -/** Modifies PropertyChange @param c to indicate what properties have changed since the last - time clear_history was called on this object. Note that not all properties have change - values - if this object has any such Property members, they will never show up in - the value of @param c. Note also that @param c is not cleared by this function. -*/ -void -Stateful::changed (PropertyChange& c) const -{ - for (OwnedPropertyList::const_iterator i = _properties.begin(); i != _properties.end(); ++i) { - i->second->diff (c); - } -} - /** Set state of some/all _properties from an XML node. * @param node Node. * @return PropertyChanges made. */ PropertyChange -Stateful::set_properties (XMLNode const & node) +Stateful::set_properties (XMLNode const & owner_state) { PropertyChange c; - - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { - if (i->second->set_state (node)) { + + for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) { + if (i->second->set_state_from_owner_state (owner_state)) { c.add (i->first); } } @@ -215,12 +198,22 @@ Stateful::set_properties (const PropertyList& property_list) PropertyChange c; PropertyList::const_iterator p; - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { - if ((p = property_list.find (i->first)) != property_list.end()) { - if (set_property (*p->second)) { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("Stateful %1 setting properties from list of %2\n", this, property_list.size())); + + for (PropertyList::const_iterator pp = property_list.begin(); pp != property_list.end(); ++pp) { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("in plist: %1\n", pp->second->property_name())); + } + + for (PropertyList::const_iterator i = property_list.begin(); i != property_list.end(); ++i) { + if ((p = _properties->find (i->first)) != _properties->end()) { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("actually setting property %1\n", p->second->property_name())); + if (set_property (*i->second)) { c.add (i->first); } - } + } else { + DEBUG_TRACE (DEBUG::Stateful, string_compose ("passed in property %1 not found in own property list\n", + i->second->property_name())); + } } post_set (); @@ -228,16 +221,21 @@ Stateful::set_properties (const PropertyList& property_list) return c; } - /** Add property states to an XML node. * @param node Node. */ void -Stateful::add_properties (XMLNode & node) +Stateful::add_properties (XMLNode& owner_state) { - for (OwnedPropertyList::iterator i = _properties.begin(); i != _properties.end(); ++i) { - i->second->add_state (node); + for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) { + i->second->add_state_to_owner_state (owner_state); } } +void +Stateful::add_property (PropertyBase& s) +{ + _properties->add (s); +} + } // namespace PBD diff --git a/libs/pbd/stateful_diff_command.cc b/libs/pbd/stateful_diff_command.cc index bf092a6594..0c7b8cc3f2 100644 --- a/libs/pbd/stateful_diff_command.cc +++ b/libs/pbd/stateful_diff_command.cc @@ -17,7 +17,10 @@ */ +#include + #include "pbd/stateful_diff_command.h" +#include "pbd/property_list.h" #include "i18n.h" using namespace std; @@ -30,24 +33,35 @@ using namespace PBD; StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr s) : _object (s) + , _before (new PropertyList) + , _after (new PropertyList) { - pair const p = s->diff (); - _before = p.first; - _after = p.second; + s->diff (*_before, *_after); } StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr s, XMLNode const & n) : _object (s) + , _before (0) + , _after (0) { - _before = new XMLNode (*n.children().front()); - _after = new XMLNode (*n.children().back()); -} + const XMLNodeList& children (n.children()); + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + if ((*i)->name() == X_("Undo")) { + _before = s->property_factory (**i); + } else if ((*i)->name() == X_("Do")) { + _after = s->property_factory (**i); + } + } + + assert (_before != 0); + assert (_after != 0); +} StatefulDiffCommand::~StatefulDiffCommand () { - delete _before; - delete _after; + delete _before; + delete _after; } void @@ -56,7 +70,10 @@ StatefulDiffCommand::operator() () boost::shared_ptr s (_object.lock()); if (s) { - s->set_state (*_after, Stateful::current_state_version); + PropertyChange changed = s->set_properties (*_after); + if (!changed.empty()) { + s->PropertyChanged (changed); + } } } @@ -66,7 +83,12 @@ StatefulDiffCommand::undo () boost::shared_ptr s (_object.lock()); if (s) { - s->set_state (*_before, Stateful::current_state_version); + std::cerr << "Undoing a stateful diff command\n"; + PropertyChange changed = s->set_properties (*_before); + if (!changed.empty()) { + std::cerr << "Sending changed\n"; + s->PropertyChanged (changed); + } } } @@ -84,8 +106,15 @@ StatefulDiffCommand::get_state () node->add_property ("obj-id", s->id().to_s()); node->add_property ("type-name", typeid(*s.get()).name()); - node->add_child_copy (*_before); - node->add_child_copy (*_after); + + XMLNode* before = new XMLNode (X_("Undo")); + XMLNode* after = new XMLNode (X_("Do")); + + _before->add_history_state (before); + _after->add_history_state (after); + + node->add_child_nocopy (*before); + node->add_child_nocopy (*after); return *node; } diff --git a/libs/pbd/undo.cc b/libs/pbd/undo.cc index a4042a7e13..a06127926f 100644 --- a/libs/pbd/undo.cc +++ b/libs/pbd/undo.cc @@ -309,7 +309,6 @@ UndoHistory::redo (unsigned int n) timersub (&end, &start, &diff); cerr << "Redo took " << diff.tv_sec << '.' << diff.tv_usec << endl; - EndUndoRedo (); /* EMIT SIGNAL */ Changed (); /* EMIT SIGNAL */ } diff --git a/libs/pbd/wscript b/libs/pbd/wscript index 8bd00bcff9..08c0f2670b 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -59,6 +59,7 @@ def build(bld): controllable.cc controllable_descriptor.cc crossthread.cc + debug.cc enumwriter.cc event_loop.cc dmalloc.cc @@ -74,6 +75,8 @@ def build(bld): mountpoint.cc pathscanner.cc pool.cc + property_factory.cc + property_list.cc pthread_utils.cc receiver.cc search_path.cc diff --git a/libs/surfaces/mackie/mackie_port.cc b/libs/surfaces/mackie/mackie_port.cc index 476e6acb81..5d8ea56a69 100644 --- a/libs/surfaces/mackie/mackie_port.cc +++ b/libs/surfaces/mackie/mackie_port.cc @@ -40,6 +40,7 @@ using namespace std; using namespace Mackie; using namespace ARDOUR; +using namespace PBD; // The MCU sysex header MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 );