summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/editor.cc6
-rw-r--r--gtk2_ardour/editor_audio_import.cc2
-rw-r--r--gtk2_ardour/editor_drag.cc62
-rw-r--r--gtk2_ardour/editor_mouse.cc14
-rw-r--r--gtk2_ardour/editor_ops.cc19
-rw-r--r--gtk2_ardour/editor_selection.cc4
-rw-r--r--gtk2_ardour/opts.cc2
-rw-r--r--libs/ardour/ardour/debug.h59
-rw-r--r--libs/ardour/ardour/playlist.h46
-rw-r--r--libs/ardour/ardour/region.h2
-rw-r--r--libs/ardour/ardour/region_factory.h7
-rw-r--r--libs/ardour/ardour/session.h5
-rw-r--r--libs/ardour/ardour/session_object.h1
-rw-r--r--libs/ardour/ardour/session_playlists.h5
-rw-r--r--libs/ardour/audioregion.cc30
-rw-r--r--libs/ardour/crossfade.cc4
-rw-r--r--libs/ardour/debug.cc116
-rw-r--r--libs/ardour/globals.cc13
-rw-r--r--libs/ardour/midi_playlist.cc1
-rw-r--r--libs/ardour/midi_ring_buffer.cc1
-rw-r--r--libs/ardour/midi_ui.cc1
-rw-r--r--libs/ardour/playlist.cc154
-rw-r--r--libs/ardour/region.cc121
-rw-r--r--libs/ardour/region_factory.cc28
-rw-r--r--libs/ardour/route_group.cc46
-rw-r--r--libs/ardour/session.cc18
-rw-r--r--libs/ardour/session_command.cc10
-rw-r--r--libs/ardour/session_object.cc5
-rw-r--r--libs/ardour/session_playlists.cc20
-rw-r--r--libs/ardour/session_state.cc13
-rw-r--r--libs/ardour/source.cc1
-rw-r--r--libs/pbd/debug.cc108
-rw-r--r--libs/pbd/pbd/debug.h57
-rw-r--r--libs/pbd/pbd/properties.h199
-rw-r--r--libs/pbd/pbd/property_basics.h116
-rw-r--r--libs/pbd/pbd/property_basics_impl.h53
-rw-r--r--libs/pbd/pbd/property_list.h75
-rw-r--r--libs/pbd/pbd/property_list_impl.h37
-rw-r--r--libs/pbd/pbd/sequence_property.h291
-rw-r--r--libs/pbd/pbd/stateful.h21
-rw-r--r--libs/pbd/pbd/stateful_diff_command.h5
-rw-r--r--libs/pbd/property_factory.cc53
-rw-r--r--libs/pbd/property_list.cc69
-rw-r--r--libs/pbd/stateful.cc78
-rw-r--r--libs/pbd/stateful_diff_command.cc53
-rw-r--r--libs/pbd/undo.cc1
-rw-r--r--libs/pbd/wscript3
-rw-r--r--libs/surfaces/mackie/mackie_port.cc1
48 files changed, 1552 insertions, 484 deletions
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<boost::shared_ptr<ARDOUR::Playlist> > 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<boost::shared_ptr<ARDOUR::Playlist> > 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<boost::shared_ptr<ARDOUR::Playlist> > 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> region, uint32_t
}
boost::shared_ptr<Playlist> playlist = existing_track->diskstream()->playlist();
- boost::shared_ptr<Region> copy (RegionFactory::create (region));
+ boost::shared_ptr<Region> 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<RegionView*> copies;
boost::shared_ptr<Diskstream> ds;
boost::shared_ptr<Playlist> from_playlist;
+ boost::shared_ptr<Playlist> to_playlist;
RegionSelection new_views;
typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
PlaylistSet modified_playlists;
@@ -828,6 +829,7 @@ RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
bool changed_tracks, changed_position;
map<RegionView*, pair<RouteTimeAxisView*, int> > final;
RouteTimeAxisView* source_tv;
+ vector<StatefulDiffCommand*> 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<RegionView*>::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<Playlist> 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<Playlist>(*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<Playlist>(*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<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
- _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
- }
+ for (vector<StatefulDiffCommand*>::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<RegionView*> (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<RegionView*> (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<Playlist>(*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 <sstream>
-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<bool> regions;
+}
+
+class RegionListProperty : public PBD::SequenceProperty<std::list<boost::shared_ptr<Region > > >
+{
+ public:
+ RegionListProperty (Playlist&);
+
+ boost::shared_ptr<Region> lookup_id (const PBD::ID& id);
+ void diff (PBD::PropertyList& before, PBD::PropertyList& after) const;
+
+ private:
+ friend class Playlist;
+ std::list<boost::shared_ptr<Region> > 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<Playlist> {
public:
typedef std::list<boost::shared_ptr<Region> > 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> 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<Playlist> copy (std::list<AudioRange>&, bool result_is_hidden = true);
int paste (boost::shared_ptr<Playlist>, 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<boost::shared_ptr<Region> > 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> region_by_id (PBD::ID);
-
bool add_region_internal (boost::shared_ptr<Region>, framepos_t position);
int remove_region_internal (boost::shared_ptr<Region>);
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<void,boost::shared_ptr<ARDOUR::Region>, 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 <map>
+#include <glibmm/thread.h>
#include "pbd/id.h"
@@ -37,8 +38,10 @@ class AudioRegion;
class RegionFactory {
public:
+ typedef std::map<PBD::ID,boost::shared_ptr<Region> > RegionMap;
static boost::shared_ptr<Region> 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<Region> create (SourceList& srcs, const XMLNode&);
private:
- static std::map<PBD::ID,boost::weak_ptr<Region> > region_map;
+ static Glib::StaticMutex region_map_lock;
+ static RegionMap region_map;
static void map_add (boost::shared_ptr<Region>);
+ static void map_remove (boost::shared_ptr<Region>);
};
}
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> region_by_id (const PBD::ID&) const;
+ boost::shared_ptr<Region> find_whole_file_parent (boost::shared_ptr<Region const>) const;
+
void add_regions (std::vector<boost::shared_ptr<Region> >&);
PBD::Signal1<void,boost::weak_ptr<Region> > 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<Region> find_whole_file_parent (boost::shared_ptr<Region const>);
-
boost::shared_ptr<Region> XMLRegionFactory (const XMLNode&, bool full);
boost::shared_ptr<AudioRegion> XMLAudioRegionFactory (const XMLNode&, bool full);
boost::shared_ptr<MidiRegion> 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 <string>
#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<Playlist> by_name (std::string name);
+ boost::shared_ptr<Playlist> by_id (const PBD::ID&);
uint32_t source_use_count (boost::shared_ptr<const Source> src) const;
template<class T> void foreach (T *obj, void (T::*func)(boost::shared_ptr<Playlist>));
void get (std::vector<boost::shared_ptr<Playlist> >&);
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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<gain_t>*>(&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;
-}
-
-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;
-}
+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::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<bool> regions;
+}
+}
+
struct ShowMeTheList {
ShowMeTheList (boost::shared_ptr<Playlist> 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<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
+ , _playlist (pl)
+{
+}
+
+boost::shared_ptr<Region>
+RegionListProperty::lookup_id (const ID& id)
+{
+ boost::shared_ptr<Region> 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<const Playlist> 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<const Playlist> other, string namestr, boo
Playlist::Playlist (boost::shared_ptr<const Playlist> 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<const RegionListProperty*>(&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<Playlist*> (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<Region>
-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<Playlist*> (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<void,boost::shared_ptr<ARDOUR::Region>,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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::whole_file.id) {
+ } else if (prop == Properties::whole_file.property_id) {
_whole_file = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::import.id) {
+ } else if (prop == Properties::import.property_id) {
_import = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::external.id) {
+ } else if (prop == Properties::external.property_id) {
_external = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::sync_marked.id) {
+ } else if (prop == Properties::sync_marked.property_id) {
_sync_marked = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::left_of_split.id) {
+ } else if (prop == Properties::left_of_split.property_id) {
_left_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::right_of_split.id) {
+ } else if (prop == Properties::right_of_split.property_id) {
_right_of_split = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::hidden.id) {
+ } else if (prop == Properties::hidden.property_id) {
bool val = dynamic_cast<const PropertyTemplate<bool>*>(&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<const PropertyTemplate<bool>*>(&prop)->val();
- } else if (prop == Properties::start.id) {
+ } else if (prop == Properties::start.property_id) {
_start = dynamic_cast<const PropertyTemplate<framepos_t>*>(&prop)->val();
- } else if (prop == Properties::length.id) {
+ } else if (prop == Properties::length.property_id) {
framecnt_t val = dynamic_cast<const PropertyTemplate<framecnt_t>* > (&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<const PropertyTemplate<framepos_t>*>(&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<const PropertyTemplate<framepos_t>*>(&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<const PropertyTemplate<layer_t>*>(&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<const PropertyTemplate<framepos_t>*>(&prop)->val();
- } else if (prop == Properties::ancestral_length.id) {
+ } else if (prop == Properties::ancestral_length.property_id) {
_ancestral_length = dynamic_cast<const PropertyTemplate<framecnt_t>*>(&prop)->val();
- } else if (prop == Properties::stretch.id) {
+ } else if (prop == Properties::stretch.property_id) {
_stretch = dynamic_cast<const PropertyTemplate<float>*>(&prop)->val();
- } else if (prop == Properties::shift.id) {
+ } else if (prop == Properties::shift.property_id) {
_shift = dynamic_cast<const PropertyTemplate<float>*>(&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<void,boost::shared_ptr<Region> > RegionFactory::CheckNewRegion;
-map<PBD::ID,boost::weak_ptr<Region> > RegionFactory::region_map;
+Glib::StaticMutex RegionFactory::region_map_lock;
+RegionFactory::RegionMap RegionFactory::region_map;
boost::shared_ptr<Region>
RegionFactory::create (boost::shared_ptr<const Region> region)
@@ -302,23 +303,40 @@ RegionFactory::create (SourceList& srcs, const XMLNode& node)
void
RegionFactory::map_add (boost::shared_ptr<Region> r)
{
- pair<ID,boost::weak_ptr<Region> > p;
+ pair<ID,boost::shared_ptr<Region> > 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<Region> 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<Region>
RegionFactory::region_by_id (const PBD::ID& id)
{
- map<ID,boost::weak_ptr<Region> >::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<Region>();
}
- 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<Region> weak_region)
}
boost::shared_ptr<Region>
-Session::find_whole_file_parent (boost::shared_ptr<Region const> child)
+Session::find_whole_file_parent (boost::shared_ptr<Region const> child) const
{
- RegionList::iterator i;
+ RegionList::const_iterator i;
boost::shared_ptr<Region> region;
Glib::Mutex::Lock lm (region_lock);
@@ -2825,6 +2825,20 @@ Session::find_whole_file_parent (boost::shared_ptr<Region const> child)
return boost::shared_ptr<Region> ();
}
+boost::shared_ptr<Region>
+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<Region> ();
+}
+
int
Session::destroy_region (boost::shared_ptr<Region> 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<Playlist> 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<const PropertyTemplate<std::string>*>(&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<Playlist>();
}
+boost::shared_ptr<Playlist>
+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<Playlist>();
+}
+
void
SessionPlaylists::unassigned (std::list<boost::shared_ptr<Playlist> > & 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<Region> 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 <cstring>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+
+#include "pbd/debug.h"
+
+#include "i18n.h"
+
+using namespace std;
+static uint64_t _debug_bit = 1;
+static std::map<const char*,uint64_t> _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<const char*,uint64_t>::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<const char*,uint64_t>::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 <stdint.h>
+
+#include <sstream>
+
+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 <glib.h>
#include "pbd/xml++.h"
+#include "pbd/property_basics.h"
+#include "pbd/property_list.h"
namespace PBD {
-typedef GQuark PropertyID;
-
-template<typename T>
-struct PropertyDescriptor {
- PropertyID id;
- typedef T value_type;
-};
-
-class PropertyChange : public std::set<PropertyID>
-{
-public:
- PropertyChange() {}
-
- template<typename T>
- PropertyChange(PropertyDescriptor<T> p) { insert (p.id); }
-
- PropertyChange(const PropertyChange& other) : std::set<PropertyID> (other) {}
-
- PropertyChange operator=(const PropertyChange& other) {
- clear ();
- insert (other.begin (), other.end ());
- return *this;
- }
-
- template<typename T>
- PropertyChange operator=(PropertyDescriptor<T> p) {
- clear ();
- insert (p.id);
- return *this;
- }
-
- template<typename T>
- bool contains (PropertyDescriptor<T> 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<typename T>
- void add (PropertyDescriptor<T> 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 T>
class PropertyTemplate : public PropertyBase
{
public:
PropertyTemplate (PropertyDescriptor<T> 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<T> 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<T> 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<class T>
@@ -222,6 +150,21 @@ public:
Property (PropertyDescriptor<T> q, T const& v)
: PropertyTemplate<T> (q, v)
{}
+
+ void diff (PropertyList& before, PropertyList& after) const {
+ if (this->_have_old) {
+ before.add (new Property<T> (this->property_id(), this->_old));
+ after.add (new Property<T> (this->property_id(), this->_current));
+ }
+ }
+
+ Property<T>* 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<T> (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<T> q)
+ : PropertyTemplate<T> (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<std::string> : public PropertyTemplate<std::string>
@@ -264,6 +213,13 @@ public:
: PropertyTemplate<std::string> (q, v)
{}
+ void diff (PropertyList& before, PropertyList& after) const {
+ if (this->_have_old) {
+ before.add (new Property<std::string> (PropertyDescriptor<std::string> (this->property_id()), this->_old));
+ after.add (new Property<std::string> (PropertyDescriptor<std::string> (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<PropertyID, PropertyBase*>
-{
-public:
- PropertyList() : _property_owner (true) {}
- virtual ~PropertyList() {
- if (_property_owner) {
- for (std::map<PropertyID, PropertyBase*>::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<typename T, typename V>
- bool add (PropertyDescriptor<T> pid, const V& v) {
- return insert (value_type (pid.id, new Property<T> (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 <glib.h>
+#include <set>
+
+#include "pbd/xml++.h"
+
+namespace PBD {
+
+class PropertyList;
+typedef GQuark PropertyID;
+
+template<typename T>
+struct PropertyDescriptor {
+ PropertyDescriptor () : property_id (0) {}
+ PropertyDescriptor (PropertyID pid) : property_id (pid) {}
+
+ PropertyID property_id;
+ typedef T value_type;
+};
+
+class PropertyChange : public std::set<PropertyID>
+{
+public:
+ PropertyChange() {}
+
+ template<typename T> PropertyChange(PropertyDescriptor<T> p);
+
+ PropertyChange(const PropertyChange& other) : std::set<PropertyID> (other) {}
+
+ PropertyChange operator=(const PropertyChange& other) {
+ clear ();
+ insert (other.begin (), other.end ());
+ return *this;
+ }
+
+ template<typename T> PropertyChange operator=(PropertyDescriptor<T> p);
+ template<typename T> bool contains (PropertyDescriptor<T> 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<typename T> void add (PropertyDescriptor<T> 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<typename T>
+PropertyChange::PropertyChange(PropertyDescriptor<T> p)
+{
+ insert (p.property_id);
+}
+
+template<typename T> PropertyChange
+PropertyChange::operator=(PropertyDescriptor<T> p)
+{
+ clear ();
+ insert (p.property_id);
+ return *this;
+}
+
+template<typename T> bool
+PropertyChange::contains (PropertyDescriptor<T> p) const
+{
+ return find (p.property_id) != end ();
+}
+
+template<typename T> void
+PropertyChange::add (PropertyDescriptor<T> 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 <map>
+
+#include "pbd/property_basics.h"
+
+class XMLNode;
+
+namespace PBD {
+class PropertyList : public std::map<PropertyID, PropertyBase*>
+{
+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<T>
+ */
+ template<typename T, typename V> bool add (PropertyDescriptor<T> 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<typename T, typename V> bool
+PropertyList::add (PropertyDescriptor<T> pid, const V& v) {
+ return insert (value_type (pid.property_id, new Property<T> (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 <iostream>
+
+#include <set>
+#include <list>
+
+#include <boost/function.hpp>
+
+#include "pbd/id.h"
+#include "pbd/property_basics.h"
+
+#include "i18n.h"
+
+namespace PBD {
+template<typename Container>
+class SequenceProperty : public PropertyBase
+{
+ public:
+ typedef std::set<typename Container::value_type> ChangeContainer;
+
+ struct ChangeRecord {
+ ChangeContainer added;
+ ChangeContainer removed;
+ };
+
+ SequenceProperty (PropertyID id, const boost::function<void(const ChangeRecord&)>& update)
+ : PropertyBase (id), _update_callback (update) {}
+
+ virtual typename Container::value_type lookup_id (const PBD::ID&) = 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<class BinaryPredicate> 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<void(const ChangeRecord&)> _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<XMLNode *, XMLNode*> 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<Stateful> _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 <stdint.h>
+#include <cstdio>
+
+#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<bool>).name()) {
+
+ PropertyDescriptor<bool> pd (id);
+ Property<bool>* p = new Property<bool> (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 <unistd.h>
+#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<XMLNode *, XMLNode *>
-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 <iostream>
+
#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<Stateful> s)
: _object (s)
+ , _before (new PropertyList)
+ , _after (new PropertyList)
{
- pair<XMLNode *, XMLNode*> const p = s->diff ();
- _before = p.first;
- _after = p.second;
+ s->diff (*_before, *_after);
}
StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<Stateful> 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<Stateful> 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<Stateful> 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 );