major changes to Region, AudioRegion, Playlist, AudioPlaylist and Crossfade state management, to try to fix undo/redo. Not finished, butthe speedups etc. are in place

git-svn-id: svn://localhost/ardour2/trunk@993 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis
2006-10-19 22:02:30 +00:00
parent 0c31e4c4f3
commit 2592a320d4
22 changed files with 396 additions and 949 deletions

View File

@@ -254,7 +254,7 @@ AudioStreamView::add_crossfade (Crossfade *crossfade)
/* first see if we already have a CrossfadeView for this Crossfade */
for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
if (&(*i)->crossfade == crossfade) {
if ((*i)->crossfade == *crossfade) {
if (!crossfades_visible) {
(*i)->hide();
} else {
@@ -313,7 +313,6 @@ AudioStreamView::redisplay_diskstream ()
list<RegionView *>::iterator i, tmp;
list<CrossfadeView*>::iterator xi, tmpx;
for (i = region_views.begin(); i != region_views.end(); ++i) {
(*i)->set_valid (false);
}
@@ -327,6 +326,7 @@ AudioStreamView::redisplay_diskstream ()
if (_trackview.is_audio_track()) {
_trackview.get_diskstream()->playlist()->foreach_region (static_cast<StreamView*>(this), &StreamView::add_region_view);
AudioPlaylist* apl = dynamic_cast<AudioPlaylist*>(_trackview.get_diskstream()->playlist());
if (apl)
apl->foreach_crossfade (this, &AudioStreamView::add_crossfade);
@@ -358,7 +358,9 @@ AudioStreamView::redisplay_diskstream ()
/* now fix layering */
playlist_modified ();
for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
region_layered (*i);
}
}
void

View File

@@ -3074,9 +3074,6 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
case OverlapNone:
cerr << "no overlap, first = " << first_frame << " last = " << last_frame << " region = "
<< clicked_regionview->region()->first_frame() << " .. " << clicked_regionview->region()->last_frame() << endl;
if (last_frame < clicked_regionview->region()->first_frame()) {
first_frame = last_frame;
last_frame = clicked_regionview->region()->last_frame();
@@ -3087,9 +3084,6 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
break;
case OverlapExternal:
cerr << "external overlap, first = " << first_frame << " last = " << last_frame << " region = "
<< clicked_regionview->region()->first_frame() << " .. " << clicked_regionview->region()->last_frame() << endl;
if (last_frame < clicked_regionview->region()->first_frame()) {
first_frame = last_frame;
last_frame = clicked_regionview->region()->last_frame();
@@ -3100,9 +3094,6 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
break;
case OverlapInternal:
cerr << "internal overlap, first = " << first_frame << " last = " << last_frame << " region = "
<< clicked_regionview->region()->first_frame() << " .. " << clicked_regionview->region()->last_frame() << endl;
if (last_frame < clicked_regionview->region()->first_frame()) {
first_frame = last_frame;
last_frame = clicked_regionview->region()->last_frame();

View File

@@ -4837,7 +4837,7 @@ Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
// playlist is frozen, so we have to update manually
playlist->StateChanged (Change (~0)); /* EMIT SIGNAL */
playlist->Modified(); /* EMIT SIGNAL */
}
gint

View File

@@ -2910,14 +2910,26 @@ Editor::cut_copy_points (CutCopyOp op)
}
}
struct PlaylistState {
Playlist* playlist;
XMLNode* before;
};
struct lt_playlist {
bool operator () (const PlaylistState& a, const PlaylistState& b) {
return a.playlist < b.playlist;
}
};
void
Editor::cut_copy_regions (CutCopyOp op)
{
typedef std::map<AudioPlaylist*,AudioPlaylist*> PlaylistMapping;
PlaylistMapping pmap;
nframes_t first_position = max_frames;
set<Playlist*> freezelist;
pair<set<Playlist*>::iterator,bool> insert_result;
set<PlaylistState, lt_playlist> freezelist;
pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
first_position = min ((*x)->region()->position(), first_position);
@@ -2925,10 +2937,15 @@ Editor::cut_copy_regions (CutCopyOp op)
if (op == Cut || op == Clear) {
AudioPlaylist *pl = dynamic_cast<AudioPlaylist*>((*x)->region()->playlist());
if (pl) {
insert_result = freezelist.insert (pl);
PlaylistState before;
before.playlist = pl;
before.before = &pl->get_state();
insert_result = freezelist.insert (before);
if (insert_result.second) {
pl->freeze ();
session->add_command (new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
}
}
}
@@ -2990,9 +3007,9 @@ Editor::cut_copy_regions (CutCopyOp op)
cut_buffer->set (foo);
}
for (set<Playlist*>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
(*pl)->thaw ();
session->add_command (new MementoCommand<Playlist>(*(*pl), 0, &(*pl)->get_state()));
for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
(*pl).playlist->thaw ();
session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
}
}

View File

@@ -244,9 +244,6 @@ RouteTimeAxisView::set_playlist (Playlist *newplaylist)
assert(pl);
modified_connection.disconnect ();
state_changed_connection.disconnect ();
state_changed_connection = pl->StateChanged.connect (mem_fun(*this, &RouteTimeAxisView::playlist_state_changed));
modified_connection = pl->Modified.connect (mem_fun(*this, &RouteTimeAxisView::playlist_modified));
}

View File

@@ -248,7 +248,6 @@ protected:
vector<RedirectAutomationLine*> redirect_automation_curves;
sigc::connection modified_connection;
sigc::connection state_changed_connection;
};
#endif /* __ardour_route_time_axis_h__ */

View File

@@ -206,9 +206,7 @@ StreamView::playlist_modified ()
{
ENSURE_GUI_THREAD (mem_fun (*this, &StreamView::playlist_modified));
for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
region_layered (*i);
}
redisplay_diskstream ();
}
void
@@ -231,20 +229,9 @@ StreamView::playlist_changed (boost::shared_ptr<Diskstream> ds)
/* catch changes */
playlist_connections.push_back (ds->playlist()->RegionAdded.connect (mem_fun (*this, &StreamView::add_region_view)));
playlist_connections.push_back (ds->playlist()->RegionRemoved.connect (mem_fun (*this, &StreamView::remove_region_view)));
playlist_connections.push_back (ds->playlist()->StateChanged.connect (mem_fun (*this, &StreamView::playlist_state_changed)));
playlist_connections.push_back (ds->playlist()->Modified.connect (mem_fun (*this, &StreamView::playlist_modified)));
}
void
StreamView::playlist_state_changed (Change ignored)
{
ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::playlist_state_changed), ignored));
redisplay_diskstream ();
}
void
StreamView::diskstream_changed ()
{

View File

@@ -116,7 +116,6 @@ protected:
virtual void redisplay_diskstream () = 0;
void diskstream_changed ();
void playlist_state_changed (ARDOUR::Change);
virtual void playlist_changed (boost::shared_ptr<ARDOUR::Diskstream>);
virtual void playlist_modified ();

View File

@@ -38,19 +38,6 @@ class AudioPlaylist : public ARDOUR::Playlist
{
public:
typedef std::list<Crossfade*> Crossfades;
private:
struct State : public ARDOUR::StateManager::State {
RegionList regions;
std::list<UndoAction> region_states;
Crossfades crossfades;
std::list<UndoAction> crossfade_states;
State (std::string why) : ARDOUR::StateManager::State (why) {}
~State ();
};
public:
AudioPlaylist (Session&, const XMLNode&, bool hidden = false);
@@ -58,35 +45,21 @@ class AudioPlaylist : public ARDOUR::Playlist
AudioPlaylist (const AudioPlaylist&, string name, bool hidden = false);
AudioPlaylist (const AudioPlaylist&, nframes_t start, nframes_t cnt, string name, bool hidden = false);
void clear (bool with_save = true);
void clear ();
nframes_t read (Sample *dst, Sample *mixdown, float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n=0);
int set_state (const XMLNode&);
UndoAction get_memento() const;
sigc::signal<void,Crossfade *> NewCrossfade;
template<class T> void foreach_crossfade (T *t, void (T::*func)(Crossfade *));
void crossfades_at (nframes_t frame, Crossfades&);
template<class T> void apply_to_history (T& obj, void (T::*method)(const ARDOUR::StateManager::StateMap&, state_id_t)) {
RegionLock rlock (this);
(obj.*method) (states, _current_state_id);
}
bool destroy_region (boost::shared_ptr<Region>);
void drop_all_states ();
protected:
/* state management */
StateManager::State* state_factory (std::string) const;
Change restore_state (StateManager::State&);
void send_state_change (Change);
/* playlist "callbacks" */
void notify_crossfade_added (Crossfade *);
void flush_notifications ();
@@ -101,7 +74,7 @@ class AudioPlaylist : public ARDOUR::Playlist
~AudioPlaylist (); /* public should use unref() */
private:
Crossfades _crossfades;
Crossfades _crossfades; /* xfades currently in use */
Crossfades _pending_xfade_adds;
void crossfade_invalidated (Crossfade*);

View File

@@ -42,18 +42,6 @@ class Session;
class AudioFilter;
class AudioSource;
struct AudioRegionState : public RegionState
{
AudioRegionState (std::string why);
Curve _fade_in;
Curve _fade_out;
Curve _envelope;
gain_t _scale_amplitude;
uint32_t _fade_in_disabled;
uint32_t _fade_out_disabled;
};
class AudioRegion : public Region
{
public:
@@ -131,8 +119,6 @@ class AudioRegion : public Region
int separate_by_channel (ARDOUR::Session&, vector<AudioRegion*>&) const;
UndoAction get_memento() const;
/* filter */
int apply (AudioFilter&);
@@ -167,9 +153,6 @@ class AudioRegion : public Region
void set_default_fade_out ();
void set_default_envelope ();
StateManager::State* state_factory (std::string why) const;
Change restore_state (StateManager::State&);
void recompute_gain_at_end ();
void recompute_gain_at_start ();
@@ -204,6 +187,9 @@ class AudioRegion : public Region
gain_t _scale_amplitude;
uint32_t _fade_in_disabled;
uint32_t _fade_out_disabled;
protected:
int set_live_state (const XMLNode&, Change&, bool send);
};
} /* namespace ARDOUR */

View File

@@ -33,7 +33,6 @@
#include <ardour/ardour.h>
#include <ardour/curve.h>
#include <ardour/audioregion.h>
#include <ardour/state_manager.h>
#include <ardour/crossfade_compare.h>
namespace ARDOUR {
@@ -41,19 +40,7 @@ namespace ARDOUR {
class AudioRegion;
class Playlist;
struct CrossfadeState : public StateManager::State {
CrossfadeState (std::string reason) : StateManager::State (reason) {}
UndoAction fade_in_memento;
UndoAction fade_out_memento;
nframes_t position;
nframes_t length;
AnchorPoint anchor_point;
bool follow_overlap;
bool active;
};
class Crossfade : public PBD::StatefulDestructible, public StateManager
class Crossfade : public PBD::StatefulDestructible
{
public:
@@ -122,6 +109,7 @@ class Crossfade : public PBD::StatefulDestructible, public StateManager
nframes_t position() const { return _position; }
sigc::signal<void,Crossfade*> Invalidated;
sigc::signal<void,Change> StateChanged;
bool covers (nframes_t frame) const {
return _position <= frame && frame < _position + _length;
@@ -129,8 +117,6 @@ class Crossfade : public PBD::StatefulDestructible, public StateManager
OverlapType coverage (nframes_t start, nframes_t end) const;
UndoAction get_memento() const;
static void set_buffer_size (nframes_t);
bool active () const { return _active; }
@@ -172,15 +158,11 @@ class Crossfade : public PBD::StatefulDestructible, public StateManager
static Sample* crossfade_buffer_out;
static Sample* crossfade_buffer_in;
void initialize (bool savestate=true);
void initialize ();
int compute (boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>, CrossfadeModel);
bool update (bool force);
StateManager::State* state_factory (std::string why) const;
Change restore_state (StateManager::State&);
void member_changed (ARDOUR::Change);
};

View File

@@ -40,14 +40,13 @@
#include <ardour/ardour.h>
#include <ardour/crossfade_compare.h>
#include <ardour/location.h>
#include <ardour/state_manager.h>
namespace ARDOUR {
class Session;
class Region;
class Playlist : public StateManager, public PBD::StatefulDestructible {
class Playlist : public PBD::StatefulDestructible {
public:
typedef list<boost::shared_ptr<Region> > RegionList;
@@ -56,9 +55,8 @@ class Playlist : public StateManager, public PBD::StatefulDestructible {
Playlist (const Playlist&, string name, bool hidden = false);
Playlist (const Playlist&, nframes_t start, nframes_t cnt, string name, bool hidden = false);
virtual void clear (bool with_save = true);
virtual void clear ();
virtual void dump () const;
virtual UndoAction get_memento() const = 0;
void ref();
void unref();
@@ -80,7 +78,7 @@ class Playlist : public StateManager, public PBD::StatefulDestructible {
/* Editing operations */
void add_region (boost::shared_ptr<Region>, nframes_t position, float times = 1, bool with_save = true);
void add_region (boost::shared_ptr<Region>, nframes_t position, float times = 1);
void remove_region (boost::shared_ptr<Region>);
void get_equivalent_regions (boost::shared_ptr<Region>, std::vector<boost::shared_ptr<Region> >&);
void get_region_list_equivalent_regions (boost::shared_ptr<Region>, std::vector<boost::shared_ptr<Region> >&);
@@ -111,8 +109,6 @@ class Playlist : public StateManager, public PBD::StatefulDestructible {
int set_state (const XMLNode&);
XMLNode& get_template ();
sigc::signal<void,boost::shared_ptr<Region> > RegionAdded;
sigc::signal<void,boost::shared_ptr<Region> > RegionRemoved;
sigc::signal<void,Playlist*,bool> InUse;
sigc::signal<void> Modified;
sigc::signal<void> NameChanged;
@@ -169,13 +165,12 @@ class Playlist : public StateManager, public PBD::StatefulDestructible {
friend class RegionLock;
RegionList regions; /* the current list of regions in the playlist */
std::set<boost::shared_ptr<Region> > all_regions; /* all regions ever added to this playlist */
string _name;
Session& _session;
mutable gint block_notifications;
mutable gint ignore_state_changes;
mutable Glib::Mutex region_lock;
RegionList pending_removals;
RegionList pending_adds;
RegionList pending_bounds;
bool pending_modified;
bool pending_length;
@@ -244,10 +239,7 @@ class Playlist : public StateManager, public PBD::StatefulDestructible {
virtual XMLNode& state (bool);
/* override state_manager::save_state so we can check in_set_state() */
void save_state (std::string why);
void maybe_save_state (std::string why);
boost::shared_ptr<Region> region_by_id (PBD::ID);
void add_region_internal (boost::shared_ptr<Region>, nframes_t position, bool delay_sort = false);

View File

@@ -28,7 +28,6 @@
#include <pbd/statefuldestructible.h>
#include <ardour/ardour.h>
#include <ardour/state_manager.h>
class XMLNode;
@@ -42,21 +41,7 @@ enum RegionEditState {
EditChangesID = 2
};
struct RegionState : public StateManager::State
{
RegionState (std::string why) : StateManager::State (why) {}
nframes_t _start;
nframes_t _length;
nframes_t _position;
uint32_t _flags;
nframes_t _sync_position;
layer_t _layer;
string _name;
mutable RegionEditState _first_edit;
};
class Region : public PBD::StatefulDestructible, public StateManager, public boost::enable_shared_from_this<Region>
class Region : public PBD::StatefulDestructible, public boost::enable_shared_from_this<Region>
{
public:
enum Flag {
@@ -92,6 +77,8 @@ class Region : public PBD::StatefulDestructible, public StateManager, public boo
static Change LayerChanged;
static Change HiddenChanged;
sigc::signal<void,Change> StateChanged;
virtual ~Region();
/* Note: changing the name of a Region does not constitute an edit */
@@ -176,8 +163,6 @@ class Region : public PBD::StatefulDestructible, public StateManager, public boo
ARDOUR::Playlist* playlist() const { return _playlist; }
virtual UndoAction get_memento() const = 0;
void set_playlist (ARDOUR::Playlist*);
virtual void lock_sources () {}
@@ -188,6 +173,7 @@ class Region : public PBD::StatefulDestructible, public StateManager, public boo
XMLNode& get_state ();
virtual XMLNode& state (bool);
virtual int set_state (const XMLNode&);
virtual int set_live_state (const XMLNode&, Change&, bool send);
virtual boost::shared_ptr<Region> get_parent() = 0;
@@ -207,15 +193,8 @@ class Region : public PBD::StatefulDestructible, public StateManager, public boo
protected:
XMLNode& get_short_state (); /* used only by Session */
/* state management */
void send_change (Change);
/* derived classes need these during their own state management calls */
void store_state (RegionState&) const;
Change restore_and_return_flags (RegionState&);
void trim_to_internal (nframes_t position, nframes_t length, void *src);
bool copied() const { return _flags & Copied; }

View File

@@ -39,10 +39,6 @@ using namespace sigc;
using namespace std;
using namespace PBD;
AudioPlaylist::State::~State ()
{
}
AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
: Playlist (session, node, hidden)
{
@@ -50,8 +46,6 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden
set_state (node);
in_set_state = false;
save_state (_("initial state"));
if (!hidden) {
PlaylistCreated (this); /* EMIT SIGNAL */
}
@@ -60,8 +54,6 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden
AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
: Playlist (session, name, hidden)
{
save_state (_("initial state"));
if (!hidden) {
PlaylistCreated (this); /* EMIT SIGNAL */
}
@@ -71,8 +63,6 @@ AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidden)
: Playlist (other, name, hidden)
{
save_state (_("initial state"));
RegionList::const_iterator in_o = other.regions.begin();
RegionList::iterator in_n = regions.begin();
@@ -118,8 +108,6 @@ AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidd
AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, nframes_t start, nframes_t cnt, string name, bool hidden)
: Playlist (other, start, cnt, name, hidden)
{
save_state (_("initial state"));
/* this constructor does NOT notify others (session) */
}
@@ -134,24 +122,7 @@ AudioPlaylist::~AudioPlaylist ()
notify_callbacks ();
for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end(); ++x) {
all_xfades.insert (*x);
}
for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
all_xfades.insert (*xf);
}
delete apstate;
}
/* delete every crossfade */
for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
delete *axf;
delete *x;
}
}
@@ -447,7 +418,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
add_crossfade (*xfade);
} else {
xfade = new Crossfade (other, region, Config->get_xfade_model(), Config->get_crossfades_active());
add_crossfade (*xfade);
}
@@ -519,7 +490,7 @@ AudioPlaylist::set_state (const XMLNode& node)
if (!in_set_state) {
Playlist::set_state (node);
}
}
nlist = node.children();
@@ -527,155 +498,60 @@ AudioPlaylist::set_state (const XMLNode& node)
child = *niter;
if (child->name() == "Crossfade") {
Crossfade *xfade;
try {
xfade = new Crossfade (*((const Playlist *)this), *child);
}
catch (failed_constructor& err) {
// cout << string_compose (_("could not create crossfade object in playlist %1"),
// _name)
// << endl;
continue;
}
Crossfades::iterator ci;
for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
if (*(*ci) == *xfade) {
break;
}
}
if (ci == _crossfades.end()) {
_crossfades.push_back (xfade);
xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
NewCrossfade(xfade);
} else {
delete xfade;
}
if (child->name() != "Crossfade") {
continue;
}
Crossfade *xfade;
try {
xfade = new Crossfade (*((const Playlist *)this), *child);
}
catch (failed_constructor& err) {
// cout << string_compose (_("could not create crossfade object in playlist %1"),
// _name)
// << endl;
continue;
}
Crossfades::iterator ci;
for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
if (*(*ci) == *xfade) {
break;
}
}
if (ci == _crossfades.end()) {
_crossfades.push_back (xfade);
xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
NewCrossfade(xfade);
} else {
/* adjust the current state of the existing crossfade */
(*ci)->set_state (*child);
/* drop the new one */
delete xfade;
}
}
return 0;
}
void
AudioPlaylist::drop_all_states ()
AudioPlaylist::clear ()
{
set<Crossfade*> all_xfades;
set<boost::shared_ptr<Region> > all_regions;
/* find every region we've ever used, and add it to the set of
all regions. same for xfades;
*/
for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
all_regions.insert (*r);
}
for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
all_xfades.insert (*xf);
}
for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
delete *i;
}
/* now remove from the "all" lists every region that is in the current list. */
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
set<boost::shared_ptr<Region> >::iterator x = all_regions.find (*i);
if (x != all_regions.end()) {
all_regions.erase (x);
}
}
/* ditto for every crossfade */
for (list<Crossfade*>::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
set<Crossfade*>::iterator x = all_xfades.find (*i);
if (x != all_xfades.end()) {
all_xfades.erase (x);
}
}
/* delete every crossfade that is left (ditto as per regions) */
for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
delete *axf;
}
/* Now do the generic thing ... */
StateManager::drop_all_states ();
}
StateManager::State*
AudioPlaylist::state_factory (std::string why) const
{
State* state = new State (why);
state->regions = regions;
state->region_states.clear ();
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
state->region_states.push_back ((*i)->get_memento());
}
state->crossfades = _crossfades;
state->crossfade_states.clear ();
for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
state->crossfade_states.push_back ((*i)->get_memento());
}
return state;
}
Change
AudioPlaylist::restore_state (StateManager::State& state)
{
{
RegionLock rlock (this);
State* apstate = dynamic_cast<State*> (&state);
in_set_state = true;
regions = apstate->regions;
for (list<UndoAction>::iterator s = apstate->region_states.begin(); s != apstate->region_states.end(); ++s) {
(*s) ();
}
_crossfades = apstate->crossfades;
for (list<UndoAction>::iterator s = apstate->crossfade_states.begin(); s != apstate->crossfade_states.end(); ++s) {
(*s) ();
}
in_set_state = false;
}
notify_length_changed ();
return Change (~0);
}
UndoAction
AudioPlaylist::get_memento () const
{
return sigc::bind (mem_fun (*(const_cast<AudioPlaylist*> (this)), &StateManager::use_state), _current_state_id);
}
void
AudioPlaylist::clear (bool with_save)
{
_crossfades.clear ();
Playlist::clear (with_save);
Playlist::clear ();
}
XMLNode&
@@ -776,52 +652,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
c = ctmp;
}
for (StateMap::iterator s = states.begin(); s != states.end(); ) {
StateMap::iterator tmp;
tmp = s;
++tmp;
State* astate = dynamic_cast<State*> (*s);
for (c = astate->crossfades.begin(); c != astate->crossfades.end(); ) {
ctmp = c;
++ctmp;
if ((*c)->involves (r)) {
unique_xfades.insert (*c);
_crossfades.erase (c);
}
c = ctmp;
}
list<UndoAction>::iterator rsi, rsitmp;
RegionList::iterator ri, ritmp;
for (ri = astate->regions.begin(), rsi = astate->region_states.begin();
ri != astate->regions.end() && rsi != astate->region_states.end();) {
ritmp = ri;
++ritmp;
rsitmp = rsi;
++rsitmp;
if (region == (*ri)) {
astate->regions.erase (ri);
astate->region_states.erase (rsi);
}
ri = ritmp;
rsi = rsitmp;
}
s = tmp;
}
for (set<Crossfade*>::iterator c = unique_xfades.begin(); c != unique_xfades.end(); ++c) {
delete *c;
}
@@ -847,8 +677,6 @@ AudioPlaylist::crossfade_changed (Change ignored)
that occured.
*/
maybe_save_state (_("xfade change"));
notify_modified ();
}
@@ -870,8 +698,6 @@ AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> re
parent_wants_notify = Playlist::region_changed (what_changed, region);
maybe_save_state (_("region modified"));
if ((parent_wants_notify || (what_changed & our_interests))) {
notify_modified ();
}

View File

@@ -58,14 +58,6 @@ Change AudioRegion::EnvelopeActiveChanged = ARDOUR::new_change();
Change AudioRegion::ScaleAmplitudeChanged = ARDOUR::new_change();
Change AudioRegion::EnvelopeChanged = ARDOUR::new_change();
AudioRegionState::AudioRegionState (string why)
: RegionState (why),
_fade_in (0.0, 2.0, 1.0, false),
_fade_out (0.0, 2.0, 1.0, false),
_envelope (0.0, 2.0, 1.0, false)
{
}
AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length)
: Region (start, length, PBD::basename_nosuffix(src->name()), 0, Region::Flag(Region::DefaultFlags|Region::External)),
_fade_in (0.0, 2.0, 1.0, false),
@@ -88,8 +80,6 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
set_default_fades ();
set_default_envelope ();
save_state ("initial state");
_envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
}
@@ -114,7 +104,6 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
set_default_fades ();
set_default_envelope ();
save_state ("initial state");
_envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
}
@@ -142,7 +131,6 @@ AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, c
set_default_fades ();
set_default_envelope ();
save_state ("initial state");
_envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
}
@@ -208,8 +196,6 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
_scale_amplitude = other->_scale_amplitude;
save_state ("initial state");
_envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
}
@@ -251,8 +237,6 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
_fade_in_disabled = 0;
_fade_out_disabled = 0;
save_state ("initial state");
_envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
}
@@ -277,8 +261,6 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod
throw failed_constructor();
}
save_state ("initial state");
_envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
}
@@ -319,8 +301,6 @@ AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
throw failed_constructor();
}
save_state ("initial state");
_envelope.StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
}
@@ -330,88 +310,6 @@ AudioRegion::~AudioRegion ()
GoingAway (); /* EMIT SIGNAL */
}
StateManager::State*
AudioRegion::state_factory (std::string why) const
{
AudioRegionState* state = new AudioRegionState (why);
Region::store_state (*state);
state->_fade_in = _fade_in;
state->_fade_out = _fade_out;
state->_envelope = _envelope;
state->_scale_amplitude = _scale_amplitude;
state->_fade_in_disabled = _fade_in_disabled;
state->_fade_out_disabled = _fade_out_disabled;
return state;
}
Change
AudioRegion::restore_state (StateManager::State& sstate)
{
AudioRegionState* state = dynamic_cast<AudioRegionState*> (&sstate);
Change what_changed = Region::restore_and_return_flags (*state);
if (_flags != Flag (state->_flags)) {
uint32_t old_flags = _flags;
_flags = Flag (state->_flags);
if ((old_flags ^ state->_flags) & EnvelopeActive) {
what_changed = Change (what_changed|EnvelopeActiveChanged);
}
}
if (!(_fade_in == state->_fade_in)) {
_fade_in = state->_fade_in;
what_changed = Change (what_changed|FadeInChanged);
}
if (!(_fade_out == state->_fade_out)) {
_fade_out = state->_fade_out;
what_changed = Change (what_changed|FadeOutChanged);
}
if (_scale_amplitude != state->_scale_amplitude) {
_scale_amplitude = state->_scale_amplitude;
what_changed = Change (what_changed|ScaleAmplitudeChanged);
}
if (_fade_in_disabled != state->_fade_in_disabled) {
if (_fade_in_disabled == 0 && state->_fade_in_disabled) {
set_fade_in_active (false);
} else if (_fade_in_disabled && state->_fade_in_disabled == 0) {
set_fade_in_active (true);
}
_fade_in_disabled = state->_fade_in_disabled;
}
if (_fade_out_disabled != state->_fade_out_disabled) {
if (_fade_out_disabled == 0 && state->_fade_out_disabled) {
set_fade_out_active (false);
} else if (_fade_out_disabled && state->_fade_out_disabled == 0) {
set_fade_out_active (true);
}
_fade_out_disabled = state->_fade_out_disabled;
}
/* XXX need a way to test stored state versus current for envelopes */
_envelope = state->_envelope;
what_changed = Change (what_changed);
return what_changed;
}
UndoAction
AudioRegion::get_memento() const
{
return sigc::bind (mem_fun (*(const_cast<AudioRegion *> (this)), &StateManager::use_state), _current_state_id);
}
bool
AudioRegion::verify_length (nframes_t len)
{
@@ -482,11 +380,7 @@ AudioRegion::set_envelope_active (bool yn)
snprintf (buf, sizeof (buf), "envelope off");
_flags = Flag (_flags & ~EnvelopeActive);
}
if (!_frozen) {
save_state (buf);
}
send_change (EnvelopeActiveChanged);
}
}
@@ -706,6 +600,8 @@ AudioRegion::state (bool full)
} else {
_fade_in.store_state (*child);
}
child->add_property (X_("active"), _fade_in_disabled ? X_("no") : X_("yes"));
child = node.add_child (X_("FadeOut"));
@@ -714,6 +610,8 @@ AudioRegion::state (bool full)
} else {
_fade_out.store_state (*child);
}
child->add_property (X_("active"), _fade_out_disabled ? X_("no") : X_("yes"));
}
child = node.add_child ("Envelope");
@@ -736,6 +634,7 @@ AudioRegion::state (bool full)
} else {
_envelope.store_state (*child);
}
} else {
child->add_property ("default", "yes");
}
@@ -748,14 +647,16 @@ AudioRegion::state (bool full)
}
int
AudioRegion::set_state (const XMLNode& node)
AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send)
{
const XMLNodeList& nlist = node.children();
const XMLProperty *prop;
LocaleGuard lg (X_("POSIX"));
Region::set_state (node);
Region::set_live_state (node, what_changed, false);
uint32_t old_flags = _flags;
if ((prop = node.property ("flags")) != 0) {
_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
@@ -763,6 +664,16 @@ AudioRegion::set_state (const XMLNode& node)
_flags = Flag (_flags & ~Region::RightOfSplit);
}
if ((old_flags ^ _flags) & Muted) {
what_changed = Change (what_changed|MuteChanged);
}
if ((old_flags ^ _flags) & Opaque) {
what_changed = Change (what_changed|OpacityChanged);
}
if ((old_flags ^ _flags) & Locked) {
what_changed = Change (what_changed|LockChanged);
}
if ((prop = node.property ("scale-gain")) != 0) {
_scale_amplitude = atof (prop->value().c_str());
} else {
@@ -798,7 +709,6 @@ AudioRegion::set_state (const XMLNode& node)
if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
set_default_fade_in ();
} else {
_fade_in.load_state (*child);
}
@@ -814,9 +724,24 @@ AudioRegion::set_state (const XMLNode& node)
}
}
if (send) {
send_change (what_changed);
}
return 0;
}
int
AudioRegion::set_state (const XMLNode& node)
{
/* Region::set_state() calls the virtual set_live_state(),
which will get us back to AudioRegion::set_live_state()
to handle the relevant stuff.
*/
return Region::set_state (node);
}
void
AudioRegion::set_fade_in_shape (FadeShape shape)
{
@@ -886,10 +811,6 @@ AudioRegion::set_fade_in (FadeShape shape, nframes_t len)
_fade_in.thaw ();
_fade_in_shape = shape;
if (!_frozen) {
save_state (_("fade in change"));
}
send_change (FadeInChanged);
}
@@ -948,10 +869,6 @@ AudioRegion::set_fade_out (FadeShape shape, nframes_t len)
_fade_out.thaw ();
_fade_out_shape = shape;
if (!_frozen) {
save_state (_("fade in change"));
}
send_change (FadeOutChanged);
}
@@ -962,13 +879,6 @@ AudioRegion::set_fade_in_length (nframes_t len)
if (changed) {
_flags = Flag (_flags & ~DefaultFadeIn);
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "fade in length changed to %u", len);
save_state (buf);
}
send_change (FadeInChanged);
}
}
@@ -980,12 +890,6 @@ AudioRegion::set_fade_out_length (nframes_t len)
if (changed) {
_flags = Flag (_flags & ~DefaultFadeOut);
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "fade out length changed to %u", len);
save_state (buf);
}
}
send_change (FadeOutChanged);
@@ -1324,12 +1228,6 @@ AudioRegion::normalize_to (float target_dB)
_scale_amplitude = target/maxamp;
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), _("normalized to %.2fdB"), target_dB);
save_state (buf);
}
/* tell the diskstream we're in */
if (_playlist) {
@@ -1344,7 +1242,6 @@ AudioRegion::normalize_to (float target_dB)
void
AudioRegion::envelope_changed (Change ignored)
{
save_state (_("envelope change"));
send_change (EnvelopeChanged);
}

View File

@@ -78,7 +78,7 @@ Auditioner::prepare_playlist ()
AudioPlaylist* const apl = dynamic_cast<AudioPlaylist*>(_diskstream->playlist());
assert(apl);
apl->clear (false);
apl->clear ();
return *apl;
}
@@ -126,8 +126,8 @@ Auditioner::audition_region (boost::shared_ptr<Region> region)
boost::shared_ptr<AudioRegion> the_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
the_region->set_position (0, this);
_diskstream->playlist()->clear (false);
_diskstream->playlist()->add_region (the_region, 0, 1, false);
_diskstream->playlist()->clear ();
_diskstream->playlist()->add_region (the_region, 0, 1);
while (_diskstream->n_channels() < the_region->n_channels()) {
audio_diskstream()->add_channel ();

View File

@@ -151,28 +151,24 @@ Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
}
_length = 0;
initialize(false);
initialize();
if (set_state (node)) {
throw failed_constructor();
}
save_state ("initial");
}
Crossfade::Crossfade (const Crossfade &orig, boost::shared_ptr<AudioRegion> newin, boost::shared_ptr<AudioRegion> newout)
: _fade_in(orig._fade_in),
_fade_out(orig._fade_out)
{
_active = orig._active;
_in_update = orig._in_update;
_length = orig._length;
_position = orig._position;
_anchor_point = orig._anchor_point;
_follow_overlap = orig._follow_overlap;
_fixed = orig._fixed;
_follow_overlap = orig._follow_overlap;
_short_xfade_length = orig._short_xfade_length;
_active = orig._active;
_in_update = orig._in_update;
_length = orig._length;
_position = orig._position;
_anchor_point = orig._anchor_point;
_follow_overlap = orig._follow_overlap;
_fixed = orig._fixed;
_in = newin;
_out = newout;
@@ -192,13 +188,10 @@ Crossfade::Crossfade (const Crossfade &orig, boost::shared_ptr<AudioRegion> newi
Crossfade::~Crossfade ()
{
for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
delete *i;
}
}
void
Crossfade::initialize (bool savestate)
Crossfade::initialize ()
{
_in_update = false;
@@ -225,14 +218,10 @@ Crossfade::initialize (bool savestate)
_fade_in.add (_length, 1.0);
_fade_in.thaw ();
// _in->StateChanged.connect (slot (*this, &Crossfade::member_changed));
// _out->StateChanged.connect (slot (*this, &Crossfade::member_changed));
_in->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed));
_out->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed));
overlap_type = _in->coverage (_out->position(), _out->last_frame());
if (savestate) {
save_state ("initial");
}
}
int
@@ -488,8 +477,7 @@ Crossfade::set_active (bool yn)
{
if (_active != yn) {
_active = yn;
save_state (_("active changed"));
send_state_changed (ActiveChanged);
StateChanged (ActiveChanged);
}
}
@@ -535,7 +523,6 @@ bool
Crossfade::update (bool force)
{
nframes_t newlen;
bool save = false;
if (_follow_overlap) {
newlen = _out->first_frame() + _out->length() - _in->first_frame();
@@ -559,41 +546,32 @@ Crossfade::update (bool force)
_length = newlen;
save = true;
}
switch (_anchor_point) {
case StartOfIn:
if (_position != _in->first_frame()) {
_position = _in->first_frame();
save = true;
}
break;
case EndOfIn:
if (_position != _in->last_frame() - _length) {
_position = _in->last_frame() - _length;
save = true;
}
break;
case EndOfOut:
if (_position != _out->last_frame() - _length) {
_position = _out->last_frame() - _length;
save = true;
}
}
if (save) {
save_state ("updated");
}
/* UI's may need to know that the overlap changed even
though the xfade length did not.
*/
send_state_changed (BoundsChanged); /* EMIT SIGNAL */
StateChanged (BoundsChanged); /* EMIT SIGNAL */
_in_update = false;
@@ -612,65 +590,6 @@ Crossfade::member_changed (Change what_changed)
}
}
Change
Crossfade::restore_state (StateManager::State& state)
{
CrossfadeState* xfstate = dynamic_cast<CrossfadeState*> (&state);
Change what_changed = Change (0);
_in_update = true;
xfstate->fade_in_memento ();
xfstate->fade_out_memento ();
if (_length != xfstate->length) {
what_changed = Change (what_changed|LengthChanged);
_length = xfstate->length;
}
if (_active != xfstate->active) {
what_changed = Change (what_changed|ActiveChanged);
_active = xfstate->active;
}
if (_position != xfstate->position) {
what_changed = Change (what_changed|PositionChanged);
_position = xfstate->position;
}
/* XXX what to do about notifications for these? I don't
think (G)UI cares about them because they are
implicit in the bounds.
*/
_follow_overlap = xfstate->follow_overlap;
_anchor_point = xfstate->anchor_point;
_in_update = false;
return Change (what_changed);
}
StateManager::State*
Crossfade::state_factory (std::string why) const
{
CrossfadeState* state = new CrossfadeState (why);
state->fade_in_memento = _fade_in.get_memento ();
state->fade_out_memento = _fade_out.get_memento ();
state->active = _active;
state->length = _length;
state->position = _position;
state->follow_overlap = _follow_overlap;
state->anchor_point = _anchor_point;
return state;
}
UndoAction
Crossfade::get_memento() const
{
return sigc::bind (mem_fun (*(const_cast<Crossfade *> (this)), &StateManager::use_state), _current_state_id);
}
XMLNode&
Crossfade::get_state ()
{
@@ -733,16 +652,26 @@ Crossfade::set_state (const XMLNode& node)
XMLNode* fo;
const XMLProperty* prop;
LocaleGuard lg (X_("POSIX"));
Change what_changed = Change (0);
nframes_t val;
if ((prop = node.property ("position")) != 0) {
_position = atoi (prop->value().c_str());
sscanf (prop->value().c_str(), "%" PRIu32, &val);
if (val != _position) {
_position = val;
what_changed = Change (what_changed | PositionChanged);
}
} else {
warning << _("old-style crossfade information - no position information") << endmsg;
_position = _in->first_frame();
}
if ((prop = node.property ("active")) != 0) {
_active = (prop->value() == "yes");
bool x = (prop->value() == "yes");
if (x != _active) {
_active = x;
what_changed = Change (what_changed | ActiveChanged);
}
} else {
_active = true;
}
@@ -767,7 +696,11 @@ Crossfade::set_state (const XMLNode& node)
if ((prop = node.property ("length")) != 0) {
_length = atol (prop->value().c_str());
sscanf (prop->value().c_str(), "%" PRIu32, &val);
if (val != _length) {
_length = atol (prop->value().c_str());
what_changed = Change (what_changed | LengthChanged);
}
} else {
@@ -790,6 +723,7 @@ Crossfade::set_state (const XMLNode& node)
/* fade in */
_fade_in.freeze ();
_fade_in.clear ();
children = fi->children();
@@ -808,9 +742,12 @@ Crossfade::set_state (const XMLNode& node)
_fade_in.add (x, y);
}
}
_fade_in.thaw ();
/* fade out */
_fade_in.freeze ();
_fade_out.clear ();
children = fo->children();
@@ -831,6 +768,10 @@ Crossfade::set_state (const XMLNode& node)
}
}
_fade_out.thaw ();
StateChanged (what_changed); /* EMIT SIGNAL */
return 0;
}
@@ -887,9 +828,7 @@ Crossfade::set_length (nframes_t len)
_length = len;
save_state ("length changed");
send_state_changed (LengthChanged);
StateChanged (LengthChanged);
return len;
}

View File

@@ -331,7 +331,6 @@ Diskstream::use_playlist (Playlist* playlist)
reset_write_sources (false);
}
plstate_connection = _playlist->StateChanged.connect (mem_fun (*this, &Diskstream::playlist_changed));
plmod_connection = _playlist->Modified.connect (mem_fun (*this, &Diskstream::playlist_modified));
plgone_connection = _playlist->GoingAway.connect (bind (mem_fun (*this, &Diskstream::playlist_deleted), _playlist));
}

View File

@@ -117,7 +117,6 @@ Playlist::Playlist (const Playlist& other, string namestr, bool hide)
subcnt = 0;
_read_data_count = 0;
_frozen = other._frozen;
save_on_thaw = false;
layer_op_counter = other.layer_op_counter;
freeze_length = other.freeze_length;
@@ -238,7 +237,6 @@ Playlist::init (bool hide)
subcnt = 0;
_read_data_count = 0;
_frozen = false;
save_on_thaw = false;
layer_op_counter = 0;
freeze_length = 0;
@@ -333,9 +331,9 @@ void
Playlist::notify_region_removed (boost::shared_ptr<Region> r)
{
if (holding_state ()) {
pending_removals.insert (pending_removals.end(), r);
pending_modified = true;
pending_length = true;
} else {
RegionRemoved (r); /* EMIT SIGNAL */
/* this might not be true, but we have to act
as though it could be.
*/
@@ -347,13 +345,14 @@ Playlist::notify_region_removed (boost::shared_ptr<Region> r)
void
Playlist::notify_region_added (boost::shared_ptr<Region> r)
{
/* the length change might not be true, but we have to act
as though it could be.
*/
if (holding_state()) {
pending_adds.insert (pending_adds.end(), r);
pending_modified = true;
pending_length = true;
} else {
RegionAdded (r); /* EMIT SIGNAL */
/* this might not be true, but we have to act
as though it could be.
*/
LengthChanged (); /* EMIT SIGNAL */
Modified (); /* EMIT SIGNAL */
}
@@ -406,22 +405,10 @@ Playlist::flush_notifications ()
/* don't increment n again - its the same list */
}
for (a = pending_adds.begin(); a != pending_adds.end(); ++a) {
dependent_checks_needed.insert (*a);
RegionAdded (*a); /* EMIT SIGNAL */
n++;
}
for (set<boost::shared_ptr<Region> >::iterator x = dependent_checks_needed.begin(); x != dependent_checks_needed.end(); ++x) {
check_dependents (*x, false);
}
for (r = pending_removals.begin(); r != pending_removals.end(); ++r) {
remove_dependents (*r);
RegionRemoved (*r); /* EMIT SIGNAL */
n++;
}
if ((freeze_length != _get_maximum_extent()) || pending_length) {
pending_length = 0;
LengthChanged(); /* EMIT SIGNAL */
@@ -437,15 +424,8 @@ Playlist::flush_notifications ()
Modified (); /* EMIT SIGNAL */
}
pending_adds.clear ();
pending_removals.clear ();
pending_bounds.clear ();
if (save_on_thaw) {
save_on_thaw = false;
save_state (last_save_reason);
}
in_flush = false;
}
@@ -454,7 +434,7 @@ Playlist::flush_notifications ()
*************************************************************/
void
Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times, bool with_save)
Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
{
RegionLock rlock (this);
@@ -493,10 +473,6 @@ Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, floa
boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
add_region_internal (sub, pos, true);
}
if (with_save) {
maybe_save_state (_("add region"));
}
}
void
@@ -515,6 +491,7 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit
timestamp_layer_op (region);
regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
all_regions.insert (region);
if (!holding_state () && !in_set_state) {
/* layers get assigned from XML state */
@@ -547,8 +524,6 @@ Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Regio
if (!holding_state ()) {
possibly_splice_unlocked ();
}
maybe_save_state (_("replace region"));
}
void
@@ -560,8 +535,6 @@ Playlist::remove_region (boost::shared_ptr<Region> region)
if (!holding_state ()) {
possibly_splice_unlocked ();
}
maybe_save_state (_("remove region"));
}
int
@@ -632,8 +605,6 @@ Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
(*i)->thaw ("separation");
}
maybe_save_state (_("separate"));
}
void
@@ -897,8 +868,6 @@ Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
(*i)->thaw ("playlist cut");
}
maybe_save_state (_("cut"));
return the_copy;
}
@@ -958,8 +927,6 @@ Playlist::paste (Playlist& other, nframes_t position, float times)
}
maybe_save_state (_("paste"));
return 0;
}
@@ -986,8 +953,6 @@ Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float
boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
add_region_internal (sub, pos, true);
}
maybe_save_state (_("duplicate"));
}
void
@@ -1041,8 +1006,6 @@ Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_pos
if (remove_region_internal (region, true)) {
return;
}
maybe_save_state (_("split"));
}
void
@@ -1192,7 +1155,7 @@ Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
}
void
Playlist::clear (bool with_save)
Playlist::clear ()
{
RegionList::iterator i;
RegionList tmp;
@@ -1206,10 +1169,6 @@ Playlist::clear (bool with_save)
for (i = tmp.begin(); i != tmp.end(); ++i) {
notify_region_removed (*i);
}
if (with_save) {
maybe_save_state (_("clear"));
}
}
/***********************************************************************
@@ -1339,8 +1298,6 @@ Playlist::mark_session_dirty ()
int
Playlist::set_state (const XMLNode& node)
{
in_set_state = true;
XMLNode *child;
XMLNodeList nlist;
XMLNodeConstIterator niter;
@@ -1350,13 +1307,15 @@ Playlist::set_state (const XMLNode& node)
boost::shared_ptr<Region> region;
string region_name;
clear (false);
in_set_state = true;
if (node.name() != "Playlist") {
in_set_state = false;
return -1;
}
freeze ();
plist = node.properties();
for (piter = plist.begin(); piter != plist.end(); ++piter) {
@@ -1372,6 +1331,11 @@ Playlist::set_state (const XMLNode& node)
}
}
{
RegionLock rl (this);
regions.clear ();
}
nlist = node.children();
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
@@ -1385,19 +1349,29 @@ Playlist::set_state (const XMLNode& node)
continue;
}
if ((region = RegionFactory::create (_session, *child, true)) == 0) {
error << _("Playlist: cannot create region from state file") << endmsg;
ID id = prop->value ();
if ((region = region_by_id (id))) {
Change what_changed = Change (0);
if (region->set_live_state (*child, what_changed, true)) {
error << _("Playlist: cannot reset region state from XML") << endmsg;
continue;
}
} else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
error << _("Playlist: cannot create region from XML") << endmsg;
continue;
}
add_region (region, region->position(), 1.0, false);
add_region (region, region->position(), 1.0);
// So that layer_op ordering doesn't get screwed up
region->set_last_layer_op( region->layer());
}
}
/* update dependents, which was not done during add_region_internal
due to in_set_state being true
@@ -1406,9 +1380,13 @@ Playlist::set_state (const XMLNode& node)
for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
check_dependents (*r, false);
}
notify_modified ();
in_set_state = false;
thaw ();
return 0;
}
@@ -1746,7 +1724,6 @@ Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
if (moved) {
_nudging = false;
maybe_save_state (_("nudged"));
notify_length_changed ();
}
@@ -1756,26 +1733,31 @@ boost::shared_ptr<Region>
Playlist::find_region (const ID& id) const
{
RegionLock rlock (const_cast<Playlist*> (this));
RegionList::const_iterator i;
boost::shared_ptr<Region> ret;
for (i = regions.begin(); i != regions.end(); ++i) {
/* searches all regions currently in use by the playlist */
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
if ((*i)->id() == id) {
ret = *i;
return *i;
}
}
return ret;
}
void
Playlist::save_state (std::string why)
{
if (!in_set_state) {
StateManager::save_state (why);
}
return boost::shared_ptr<Region> ();
}
boost::shared_ptr<Region>
Playlist::region_by_id (ID id)
{
/* searches all regions ever added to this playlist */
for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
if ((*i)->id() == id) {
return *i;
}
}
return boost::shared_ptr<Region> ();
}
void
Playlist::dump () const
{
@@ -1811,13 +1793,3 @@ Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
region->set_last_layer_op (++layer_op_counter);
}
void
Playlist::maybe_save_state (string why)
{
if (holding_state ()) {
save_on_thaw = true;
last_save_reason = why;
} else {
save_state (why);
}
}

View File

@@ -64,7 +64,6 @@ Region::Region (nframes_t start, nframes_t length, const string& name, layer_t l
_length = length;
_position = 0;
_layer = layer;
_current_state_id = 0;
_read_data_count = 0;
_first_edit = EditChangesNothing;
_last_layer_op = 0;
@@ -90,7 +89,6 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
_position = 0;
_layer = layer;
_flags = Flag (flags & ~(Locked|WholeFile|Hidden));
_current_state_id = 0;
_first_edit = EditChangesNothing;
_last_layer_op = 0;
}
@@ -120,7 +118,6 @@ Region::Region (boost::shared_ptr<const Region> other)
_position = other->_position;
_layer = other->_layer;
_flags = Flag (other->_flags & ~Locked);
_current_state_id = 0;
_last_layer_op = other->_last_layer_op;
}
@@ -137,7 +134,6 @@ Region::Region (const XMLNode& node)
_position = 0;
_layer = 0;
_flags = Flag (0);
_current_state_id = 0;
_first_edit = EditChangesNothing;
if (set_state (node)) {
@@ -156,70 +152,8 @@ Region::set_playlist (Playlist* pl)
_playlist = pl;
}
void
Region::store_state (RegionState& state) const
{
state._start = _start;
state._length = _length;
state._position = _position;
state._flags = _flags;
state._sync_position = _sync_position;
state._layer = _layer;
state._name = _name;
state._first_edit = _first_edit;
}
Change
Region::restore_and_return_flags (RegionState& state)
{
Change what_changed = Change (0);
{
Glib::Mutex::Lock lm (lock);
if (_start != state._start) {
what_changed = Change (what_changed|StartChanged);
_start = state._start;
}
if (_length != state._length) {
what_changed = Change (what_changed|LengthChanged);
_length = state._length;
}
if (_position != state._position) {
what_changed = Change (what_changed|PositionChanged);
_position = state._position;
}
if (_sync_position != state._sync_position) {
_sync_position = state._sync_position;
what_changed = Change (what_changed|SyncOffsetChanged);
}
if (_layer != state._layer) {
what_changed = Change (what_changed|LayerChanged);
_layer = state._layer;
}
uint32_t old_flags = _flags;
_flags = Flag (state._flags);
if ((old_flags ^ state._flags) & Muted) {
what_changed = Change (what_changed|MuteChanged);
}
if ((old_flags ^ state._flags) & Opaque) {
what_changed = Change (what_changed|OpacityChanged);
}
if ((old_flags ^ state._flags) & Locked) {
what_changed = Change (what_changed|LockChanged);
}
_first_edit = state._first_edit;
}
return what_changed;
}
void
Region::set_name (string str)
{
if (_name != str) {
_name = str;
@@ -257,10 +191,6 @@ Region::set_length (nframes_t len, void *src)
if (!_frozen) {
recompute_at_end ();
char buf[64];
snprintf (buf, sizeof (buf), "length set to %u", len);
save_state (buf);
}
send_change (LengthChanged);
@@ -328,12 +258,6 @@ Region::set_position (nframes_t pos, void *src)
if (max_frames - _length < _position) {
_length = max_frames - _position;
}
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "position set to %u", pos);
save_state (buf);
}
}
/* do this even if the position is the same. this helps out
@@ -352,12 +276,6 @@ Region::set_position_on_top (nframes_t pos, void *src)
if (_position != pos) {
_position = pos;
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "position set to %u", pos);
save_state (buf);
}
}
_playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
@@ -394,12 +312,6 @@ Region::nudge_position (long n, void *src)
}
}
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "position set to %u", _position);
save_state (buf);
}
send_change (PositionChanged);
}
@@ -424,12 +336,6 @@ Region::set_start (nframes_t pos, void *src)
_flags = Region::Flag (_flags & ~WholeFile);
first_edit ();
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "start set to %u", pos);
save_state (buf);
}
send_change (StartChanged);
}
}
@@ -480,12 +386,6 @@ Region::trim_start (nframes_t new_position, void *src)
_flags = Region::Flag (_flags & ~WholeFile);
first_edit ();
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "slipped start to %u", _start);
save_state (buf);
}
send_change (StartChanged);
}
@@ -619,13 +519,6 @@ Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
}
if (what_changed) {
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
save_state (buf);
}
send_change (what_changed);
}
}
@@ -656,16 +549,6 @@ Region::set_muted (bool yn)
_flags = Flag (_flags & ~Muted);
}
if (!_frozen) {
char buf[64];
if (yn) {
snprintf (buf, sizeof (buf), "muted");
} else {
snprintf (buf, sizeof (buf), "unmuted");
}
save_state (buf);
}
send_change (MuteChanged);
}
}
@@ -674,16 +557,10 @@ void
Region::set_opaque (bool yn)
{
if (opaque() != yn) {
if (!_frozen) {
char buf[64];
if (yn) {
snprintf (buf, sizeof (buf), "opaque");
_flags = Flag (_flags|Opaque);
} else {
snprintf (buf, sizeof (buf), "translucent");
_flags = Flag (_flags & ~Opaque);
}
save_state (buf);
if (yn) {
_flags = Flag (_flags|Opaque);
} else {
_flags = Flag (_flags & ~Opaque);
}
send_change (OpacityChanged);
}
@@ -693,16 +570,10 @@ void
Region::set_locked (bool yn)
{
if (locked() != yn) {
if (!_frozen) {
char buf[64];
if (yn) {
snprintf (buf, sizeof (buf), "locked");
_flags = Flag (_flags|Locked);
} else {
snprintf (buf, sizeof (buf), "unlocked");
_flags = Flag (_flags & ~Locked);
}
save_state (buf);
if (yn) {
_flags = Flag (_flags|Locked);
} else {
_flags = Flag (_flags & ~Locked);
}
send_change (LockChanged);
}
@@ -721,10 +592,7 @@ Region::set_sync_position (nframes_t absolute_pos)
_flags = Flag (_flags|SyncMarked);
if (!_frozen) {
char buf[64];
maybe_uncopy ();
snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
save_state (buf);
}
send_change (SyncOffsetChanged);
}
@@ -738,7 +606,6 @@ Region::clear_sync_position ()
if (!_frozen) {
maybe_uncopy ();
save_state ("sync point removed");
}
send_change (SyncOffsetChanged);
}
@@ -842,12 +709,6 @@ Region::set_layer (layer_t l)
if (_layer != l) {
_layer = l;
if (!_frozen) {
char buf[64];
snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
save_state (buf);
}
send_change (LayerChanged);
}
}
@@ -857,7 +718,8 @@ Region::state (bool full_state)
{
XMLNode *node = new XMLNode ("Region");
char buf[64];
char* fe;
_id.print (buf, sizeof (buf));
node->add_property ("id", buf);
node->add_property ("name", _name);
@@ -867,6 +729,20 @@ Region::state (bool full_state)
node->add_property ("length", buf);
snprintf (buf, sizeof (buf), "%u", _position);
node->add_property ("position", buf);
switch (_first_edit) {
case EditChangesNothing:
fe = X_("nothing");
break;
case EditChangesName:
fe = X_("name");
break;
case EditChangesID:
fe = X_("id");
break;
}
node->add_property ("first_edit", fe);
/* note: flags are stored by derived classes */
@@ -885,54 +761,83 @@ Region::get_state ()
}
int
Region::set_state (const XMLNode& node)
Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
{
const XMLNodeList& nlist = node.children();
const XMLProperty *prop;
nframes_t val;
if (_extra_xml) {
delete _extra_xml;
_extra_xml = 0;
}
if ((prop = node.property ("id")) == 0) {
error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
return -1;
}
_id = prop->value();
/* this is responsible for setting those aspects of Region state
that are mutable after construction.
*/
if ((prop = node.property ("name")) == 0) {
error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
return -1;
}
_name = prop->value();
if ((prop = node.property ("start")) != 0) {
sscanf (prop->value().c_str(), "%" PRIu32, &_start);
sscanf (prop->value().c_str(), "%" PRIu32, &val);
if (val != _start) {
what_changed = Change (what_changed|StartChanged);
_start = val;
}
} else {
_start = 0;
}
if ((prop = node.property ("length")) != 0) {
sscanf (prop->value().c_str(), "%" PRIu32, &_length);
sscanf (prop->value().c_str(), "%" PRIu32, &val);
if (val != _length) {
what_changed = Change (what_changed|LengthChanged);
_length = val;
}
} else {
_length = 1;
}
if ((prop = node.property ("position")) != 0) {
sscanf (prop->value().c_str(), "%" PRIu32, &_position);
sscanf (prop->value().c_str(), "%" PRIu32, &val);
if (val != _position) {
what_changed = Change (what_changed|PositionChanged);
_position = val;
}
} else {
_position = 0;
}
if ((prop = node.property ("layer")) != 0) {
_layer = (layer_t) atoi (prop->value().c_str());
layer_t x;
x = (layer_t) atoi (prop->value().c_str());
if (x != _layer) {
what_changed = Change (what_changed|LayerChanged);
_layer = x;
}
} else {
_layer = 0;
}
/* note: derived classes set flags */
if ((prop = node.property ("sync-position")) != 0) {
sscanf (prop->value().c_str(), "%" PRIu32, &_sync_position);
sscanf (prop->value().c_str(), "%" PRIu32, &val);
if (val != _sync_position) {
what_changed = Change (what_changed|SyncOffsetChanged);
_sync_position = val;
}
} else {
_sync_position = _start;
}
/* XXX FIRST EDIT !!! */
/* note: derived classes set flags */
if (_extra_xml) {
delete _extra_xml;
_extra_xml = 0;
}
for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLNode *child;
@@ -945,7 +850,31 @@ Region::set_state (const XMLNode& node)
}
}
if (send) {
send_change (what_changed);
}
return 0;
}
int
Region::set_state (const XMLNode& node)
{
const XMLProperty *prop;
Change what_changed = Change (0);
/* ID is not allowed to change, ever */
if ((prop = node.property ("id")) == 0) {
error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
return -1;
}
_id = prop->value();
_first_edit = EditChangesNothing;
set_live_state (node, what_changed, true);
return 0;
}
@@ -985,7 +914,6 @@ Region::thaw (const string& why)
recompute_at_end ();
}
save_state (why);
StateChanged (what_changed);
}
@@ -1000,7 +928,7 @@ Region::send_change (Change what_changed)
}
}
StateManager::send_state_changed (what_changed);
StateChanged (what_changed);
}
void

View File

@@ -28,22 +28,23 @@ Command *Session::memento_command_factory(XMLNode *n)
id = PBD::ID(n->property("obj_id")->value());
/* get before/after */
if (n->name() == "MementoCommand")
{
before = new XMLNode(*n->children().front());
after = new XMLNode(*n->children().back());
child = before;
} else if (n->name() == "MementoUndoCommand")
{
before = new XMLNode(*n->children().front());
child = before;
}
else if (n->name() == "MementoRedoCommand")
{
after = new XMLNode(*n->children().front());
child = after;
}
if (n->name() == "MementoCommand") {
before = new XMLNode(*n->children().front());
after = new XMLNode(*n->children().back());
child = before;
} else if (n->name() == "MementoUndoCommand") {
before = new XMLNode(*n->children().front());
child = before;
} else if (n->name() == "MementoRedoCommand") {
after = new XMLNode(*n->children().front());
child = after;
} else if (n->name() == "PlaylistCommand") {
before = new XMLNode(*n->children().front());
after = new XMLNode(*n->children().back());
child = before;
}
if (!child)
{
error << _("Tried to reconstitute a MementoCommand with no contents, failing. id=") << id.to_s() << endmsg;
@@ -53,43 +54,32 @@ Command *Session::memento_command_factory(XMLNode *n)
/* create command */
string obj_T = n->children().front()->name();
if (obj_T == "AudioRegion" || obj_T == "Region")
{
if (audio_regions.count(id))
return new MementoCommand<AudioRegion>(*audio_regions[id], before, after);
if (obj_T == "AudioRegion" || obj_T == "Region") {
if (audio_regions.count(id))
return new MementoCommand<AudioRegion>(*audio_regions[id], before, after);
} else if (obj_T == "AudioSource") {
if (audio_sources.count(id))
return new MementoCommand<AudioSource>(*audio_sources[id], before, after);
} else if (obj_T == "Location") {
return new MementoCommand<Location>(*_locations.get_location_by_id(id), before, after);
} else if (obj_T == "Locations") {
return new MementoCommand<Locations>(_locations, before, after);
} else if (obj_T == "TempoMap") {
return new MementoCommand<TempoMap>(*_tempo_map, before, after);
} else if (obj_T == "Playlist" || obj_T == "AudioPlaylist") {
if (Playlist *pl = playlist_by_name(child->property("name")->value()))
return new MementoCommand<Playlist>(*pl, before, after);
} else if (obj_T == "Route") { // includes AudioTrack
return new MementoCommand<Route>(*route_by_id(id), before, after);
} else if (obj_T == "Curve") {
if (curves.count(id))
return new MementoCommand<Curve>(*curves[id], before, after);
} else if (obj_T == "AutomationList") {
if (automation_lists.count(id))
return new MementoCommand<AutomationList>(*automation_lists[id], before, after);
} else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits here
return new MementoCommand<StatefulDestructible>(*registry[id], before, after);
}
else if (obj_T == "AudioSource")
{
if (audio_sources.count(id))
return new MementoCommand<AudioSource>(*audio_sources[id], before, after);
}
else if (obj_T == "Location")
return new MementoCommand<Location>(*_locations.get_location_by_id(id), before, after);
else if (obj_T == "Locations")
return new MementoCommand<Locations>(_locations, before, after);
else if (obj_T == "TempoMap")
return new MementoCommand<TempoMap>(*_tempo_map, before, after);
else if (obj_T == "Playlist" || obj_T == "AudioPlaylist")
{
if (Playlist *pl = playlist_by_name(child->property("name")->value()))
return new MementoCommand<Playlist>(*pl, before, after);
}
else if (obj_T == "Route") // inlcudes AudioTrack
return new MementoCommand<Route>(*route_by_id(id), before, after);
else if (obj_T == "Curve")
{
if (curves.count(id))
return new MementoCommand<Curve>(*curves[id], before, after);
}
else if (obj_T == "AutomationList")
{
if (automation_lists.count(id))
return new MementoCommand<AutomationList>(*automation_lists[id], before, after);
}
// For Editor and AutomationLine which are off-limits here
else if (registry.count(id))
return new MementoCommand<StatefulDestructible>(*registry[id], before, after);
/* we failed */
error << _("could not reconstitute MementoCommand from XMLNode. id=") << id.to_s() << endmsg;

View File

@@ -2434,13 +2434,7 @@ Session::cleanup_sources (Session::cleanup_report& rep)
delete *x;
}
/* step 2: clear the undo/redo history for all playlists */
for (PlaylistList::iterator x = playlists.begin(); x != playlists.end(); ++x) {
(*x)->drop_all_states ();
}
/* step 3: find all un-referenced sources */
/* step 2: find all un-referenced sources */
rep.paths.clear ();
rep.space = 0;
@@ -2470,7 +2464,7 @@ Session::cleanup_sources (Session::cleanup_report& rep)
i = tmp;
}
/* Step 4: get rid of all regions in the region list that use any dead sources
/* Step 3: get rid of all regions in the region list that use any dead sources
in case the sources themselves don't go away (they might be referenced in
other snapshots).
*/
@@ -2863,41 +2857,39 @@ Session::restore_history (string snapshot_name)
/* replace history */
history.clear();
for (XMLNodeConstIterator it = tree.root()->children().begin();
it != tree.root()->children().end();
it++)
{
XMLNode *t = *it;
UndoTransaction* ut = new UndoTransaction ();
struct timeval tv;
ut->set_name(t->property("name")->value());
stringstream ss(t->property("tv_sec")->value());
ss >> tv.tv_sec;
ss.str(t->property("tv_usec")->value());
ss >> tv.tv_usec;
ut->set_timestamp(tv);
for (XMLNodeConstIterator it = tree.root()->children().begin(); it != tree.root()->children().end(); it++) {
XMLNode *t = *it;
UndoTransaction* ut = new UndoTransaction ();
struct timeval tv;
ut->set_name(t->property("name")->value());
stringstream ss(t->property("tv_sec")->value());
ss >> tv.tv_sec;
ss.str(t->property("tv_usec")->value());
ss >> tv.tv_usec;
ut->set_timestamp(tv);
for (XMLNodeConstIterator child_it = t->children().begin();
child_it != t->children().end();
child_it++)
{
XMLNode *n = *child_it;
Command *c;
if (n->name() == "MementoCommand" ||
n->name() == "MementoUndoCommand" ||
n->name() == "MementoRedoCommand") {
if ((c = memento_command_factory(n))) {
ut->add_command(c);
}
} else {
error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
}
}
for (XMLNodeConstIterator child_it = t->children().begin();
child_it != t->children().end();
child_it++)
{
XMLNode *n = *child_it;
Command *c;
if (n->name() == "MementoCommand" ||
n->name() == "MementoUndoCommand" ||
n->name() == "MementoRedoCommand")
{
c = memento_command_factory(n);
if (c)
ut->add_command(c);
}
else
{
error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
}
}
history.add(ut);
history.add (ut);
}
return 0;