Use a list of ControlPoints to hold the automation selection,

rather than a time range.  This makes more sense now that we
display every point on an automation line, rather than just
a subset.  Makes the code a fair bit simpler, and should fix
some unexpected behaviours, especially when cutting automation
points.


git-svn-id: svn://localhost/ardour2/branches/3.0@12054 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2012-04-22 14:03:07 +00:00
parent 82c867bf2a
commit a4434809e1
13 changed files with 139 additions and 365 deletions

View File

@ -731,38 +731,6 @@ AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& /*resul
// hmmm ....
}
/** Take a PointSelection and find ControlPoints that fall within it */
list<ControlPoint*>
AutomationLine::point_selection_to_control_points (PointSelection const & s)
{
list<ControlPoint*> cp;
for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
if (i->track != &trackview) {
continue;
}
double const bot = (1 - i->high_fract) * trackview.current_height ();
double const top = (1 - i->low_fract) * trackview.current_height ();
for (vector<ControlPoint*>::iterator j = control_points.begin(); j != control_points.end(); ++j) {
double const rstart = trackview.editor().frame_to_unit (_time_converter->to (i->start) - _offset);
double const rend = trackview.editor().frame_to_unit (_time_converter->to (i->end) - _offset);
if ((*j)->get_x() >= rstart && (*j)->get_x() <= rend) {
if ((*j)->get_y() >= bot && (*j)->get_y() <= top) {
cp.push_back (*j);
}
}
}
}
return cp;
}
void
AutomationLine::set_selected_points (PointSelection const & points)
{
@ -770,11 +738,8 @@ AutomationLine::set_selected_points (PointSelection const & points)
(*i)->set_selected (false);
}
if (!points.empty()) {
list<ControlPoint*> cp = point_selection_to_control_points (points);
for (list<ControlPoint*>::iterator i = cp.begin(); i != cp.end(); ++i) {
(*i)->set_selected (true);
}
for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
(*i)->set_selected (true);
}
set_colors ();
@ -1147,13 +1112,19 @@ AutomationLine::get_point_x_range () const
pair<framepos_t, framepos_t> r (max_framepos, 0);
for (AutomationList::const_iterator i = the_list()->begin(); i != the_list()->end(); ++i) {
r.first = min (r.first, _time_converter->to ((*i)->when) + _offset + _time_converter->origin_b ());
r.second = max (r.second, _time_converter->to ((*i)->when) + _offset + _time_converter->origin_b ());
r.first = min (r.first, session_position (i));
r.second = max (r.second, session_position (i));
}
return r;
}
framepos_t
AutomationLine::session_position (AutomationList::const_iterator p) const
{
return _time_converter->to ((*p)->when) + _offset + _time_converter->origin_b ();
}
void
AutomationLine::set_offset (framepos_t off)
{

View File

@ -66,7 +66,6 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
void reset ();
void clear ();
std::list<ControlPoint*> point_selection_to_control_points (PointSelection const &);
void set_selected_points (PointSelection const &);
void get_selectables (ARDOUR::framepos_t, ARDOUR::framepos_t, double, double, std::list<Selectable*>&);
void get_inverted_selectables (Selection&, std::list<Selectable*>& results);
@ -145,6 +144,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
void set_offset (ARDOUR::framecnt_t);
void set_width (ARDOUR::framecnt_t);
framepos_t session_position (ARDOUR::AutomationList::const_iterator) const;
protected:
std::string _name;
@ -153,7 +154,7 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
boost::shared_ptr<ARDOUR::AutomationList> alist;
Evoral::TimeConverter<double, ARDOUR::framepos_t>* _time_converter;
/** true if _time_converter belongs to us (ie we should delete it) */
/** true if _time_converter belongs to us (ie we should delete it on destruction) */
bool _our_time_converter;
bool _visible : 1;

View File

@ -44,6 +44,7 @@
#include "rgb_macros.h"
#include "point_selection.h"
#include "canvas_impl.h"
#include "control_point.h"
#include "utils.h"
#include "i18n.h"
@ -592,172 +593,6 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when,
_session->set_dirty ();
}
void
AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
{
list<boost::shared_ptr<AutomationLine> > lines;
if (_line) {
lines.push_back (_line);
} else if (_view) {
lines = _view->get_lines ();
}
for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
cut_copy_clear_one (**i, selection, op);
}
}
void
AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
{
boost::shared_ptr<Evoral::ControlList> what_we_got;
boost::shared_ptr<AutomationList> alist (line.the_list());
XMLNode &before = alist->get_state();
/* convert time selection to automation list model coordinates */
const Evoral::TimeConverter<double, ARDOUR::framepos_t>& tc = line.time_converter ();
double const start = tc.from (selection.time.front().start - tc.origin_b ());
double const end = tc.from (selection.time.front().end - tc.origin_b ());
switch (op) {
case Delete:
if (alist->cut (start, end) != 0) {
_session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
}
break;
case Cut:
if ((what_we_got = alist->cut (start, end)) != 0) {
_editor.get_cut_buffer().add (what_we_got);
_session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
}
break;
case Copy:
if ((what_we_got = alist->copy (start, end)) != 0) {
_editor.get_cut_buffer().add (what_we_got);
}
break;
case Clear:
if ((what_we_got = alist->cut (start, end)) != 0) {
_session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
}
break;
}
if (what_we_got) {
for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
double when = (*x)->when;
double val = (*x)->value;
line.model_to_view_coord (when, val);
(*x)->when = when;
(*x)->value = val;
}
}
}
void
AutomationTimeAxisView::reset_objects (PointSelection& selection)
{
list<boost::shared_ptr<AutomationLine> > lines;
if (_line) {
lines.push_back (_line);
} else if (_view) {
lines = _view->get_lines ();
}
for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
reset_objects_one (**i, selection);
}
}
void
AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
{
boost::shared_ptr<AutomationList> alist(line.the_list());
_session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
if ((*i).track != this) {
continue;
}
alist->reset_range ((*i).start, (*i).end);
}
}
void
AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
{
list<boost::shared_ptr<AutomationLine> > lines;
if (_line) {
lines.push_back (_line);
} else if (_view) {
lines = _view->get_lines ();
}
for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
cut_copy_clear_objects_one (**i, selection, op);
}
}
void
AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
{
boost::shared_ptr<Evoral::ControlList> what_we_got;
boost::shared_ptr<AutomationList> alist(line.the_list());
XMLNode &before = alist->get_state();
for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
if ((*i).track != this) {
continue;
}
switch (op) {
case Delete:
if (alist->cut ((*i).start, (*i).end) != 0) {
_session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
}
break;
case Cut:
if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
_editor.get_cut_buffer().add (what_we_got);
_session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
}
break;
case Copy:
if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
_editor.get_cut_buffer().add (what_we_got);
}
break;
case Clear:
if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
_session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
}
break;
}
}
delete &before;
if (what_we_got) {
for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
double when = (*x)->when;
double val = (*x)->value;
line.model_to_view_coord (when, val);
(*x)->when = when;
(*x)->value = val;
}
}
}
/** Paste a selection.
* @param pos Position to paste to (session frames).
* @param times Number of times to paste.
@ -794,25 +629,10 @@ AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float t
return false;
}
/* Make a copy of the list because we have to scale the
values from view coordinates to model coordinates, and we're
not supposed to modify the points in the selection.
*/
AutomationList copy (**p);
for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
double when = (*x)->when;
double val = (*x)->value;
line.view_to_model_coord (when, val);
(*x)->when = when;
(*x)->value = val;
}
double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ());
XMLNode &before = alist->get_state();
alist->paste (copy, model_pos, times);
alist->paste (**p, model_pos, times);
_session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
return true;

View File

@ -91,10 +91,7 @@ class AutomationTimeAxisView : public TimeAxisView {
/* editing operations */
void cut_copy_clear (Selection&, Editing::CutCopyOp);
void cut_copy_clear_objects (PointSelection&, Editing::CutCopyOp);
bool paste (ARDOUR::framepos_t, float times, Selection&, size_t nth);
void reset_objects (PointSelection&);
int set_state (const XMLNode&, int version);
@ -169,10 +166,7 @@ class AutomationTimeAxisView : public TimeAxisView {
void build_display_menu ();
void cut_copy_clear_one (AutomationLine&, Selection&, Editing::CutCopyOp);
void cut_copy_clear_objects_one (AutomationLine&, PointSelection&, Editing::CutCopyOp);
bool paste_one (AutomationLine&, ARDOUR::framepos_t, float times, Selection&, size_t nth);
void reset_objects_one (AutomationLine&, PointSelection&);
void route_going_away ();
void set_automation_state (ARDOUR::AutoState);

View File

@ -30,6 +30,8 @@ using namespace ARDOUR;
using namespace PBD;
using namespace Gnome; // for Canvas
PBD::Signal1<void, ControlPoint *> ControlPoint::CatchDeletion;
ControlPoint::ControlPoint (AutomationLine& al)
: _line (al)
{
@ -82,6 +84,8 @@ ControlPoint::ControlPoint (const ControlPoint& other, bool /*dummy_arg_to_force
ControlPoint::~ControlPoint ()
{
CatchDeletion (this); /* EMIT SIGNAL */
delete _item;
}

View File

@ -83,6 +83,8 @@ class ControlPoint : public Selectable
ARDOUR::AutomationList::iterator model() const { return _model; }
AutomationLine& line() const { return _line; }
static PBD::Signal1<void, ControlPoint *> CatchDeletion;
private:
ArdourCanvas::SimpleRect* _item;
AutomationLine& _line;

View File

@ -63,6 +63,7 @@
#include "route_time_axis.h"
#include "audio_time_axis.h"
#include "automation_time_axis.h"
#include "control_point.h"
#include "streamview.h"
#include "audio_streamview.h"
#include "audio_region_view.h"
@ -3721,19 +3722,87 @@ Editor::cut_copy (CutCopyOp op)
}
}
struct AutomationRecord {
AutomationRecord () : state (0) {}
AutomationRecord (XMLNode* s) : state (s) {}
XMLNode* state; ///< state before any operation
boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
};
/** Cut, copy or clear selected automation points.
* @param op Operation (Cut, Copy or Clear)
* @param op Operation (Cut, Copy or Clear)
*/
void
Editor::cut_copy_points (CutCopyOp op)
{
if (selection->points.empty ()) {
return;
}
/* XXX: not ideal, as there may be more than one track involved in the point selection */
_last_cut_copy_source_track = &selection->points.front()->line().trackview;
/* Keep a record of the AutomationLists that we end up using in this operation */
typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
Lists lists;
/* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
if (lists.find (al) == lists.end ()) {
/* We haven't seen this list yet, so make a record for it. This includes
taking a copy of its current state, in case this is needed for undo later.
*/
lists[al] = AutomationRecord (&al->get_state ());
}
}
AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>((*i).track);
_last_cut_copy_source_track = atv;
if (op == Cut || op == Copy) {
/* This operation will involve putting things in the cut buffer, so create an empty
ControlList for each of our source lists to put the cut buffer data in.
*/
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
i->second.copy = i->first->create (i->first->parameter ());
}
if (atv) {
atv->cut_copy_clear_objects (selection->points, op);
/* Add all selected points to the relevant copy ControlLists */
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
AutomationList::const_iterator j = (*i)->model ();
lists[al].copy->add ((*j)->when, (*j)->value);
}
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
/* Correct this copy list so that it starts at time 0 */
double const start = i->second.copy->front()->when;
for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
(*j)->when -= start;
}
/* And add it to the cut buffer */
cut_buffer->add (i->second.copy);
}
}
if (op == Delete || op == Cut) {
/* This operation needs to remove things from the main AutomationList, so do that now */
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
i->first->freeze ();
}
/* Remove each selected point from its AutomationList */
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
al->erase ((*i)->model ());
}
/* Thaw the lists and add undo records for them */
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
boost::shared_ptr<AutomationList> al = i->first;
al->thaw ();
_session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
}
}
}
@ -4229,18 +4298,13 @@ Editor::duplicate_selection (float times)
commit_reversible_command ();
}
/** Reset all selected points to the relevant default value */
void
Editor::reset_point_selection ()
{
/* reset all selected points to the relevant default value */
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>((*i).track);
if (atv) {
atv->reset_objects (selection->points);
}
ARDOUR::AutomationList::iterator j = (*i)->model ();
(*j)->value = (*i)->line().the_list()->default_value ();
}
}

View File

@ -21,11 +21,10 @@
#define __ardour_gtk_point_selection_h__
#include <list>
#include <boost/noncopyable.hpp>
#include "automation_range.h"
class ControlPoint;
struct PointSelection : public std::list<AutomationRange>
struct PointSelection : public std::list<ControlPoint *>
{
};

View File

@ -62,6 +62,9 @@ Selection::Selection (const PublicEditor* e)
void (Selection::*marker_remove)(Marker*) = &Selection::remove;
Marker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, ui_bind (marker_remove, this, _1), gui_context());
void (Selection::*point_remove)(ControlPoint*) = &Selection::remove;
ControlPoint::CatchDeletion.connect (*this, MISSING_INVALIDATOR, ui_bind (point_remove, this, _1), gui_context());
}
#if 0
@ -516,7 +519,6 @@ Selection::add (boost::shared_ptr<Evoral::ControlList> cl)
if (!al) {
warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
return;
return;
}
if (find (lines.begin(), lines.end(), al) == lines.end()) {
lines.push_back (al);
@ -536,6 +538,15 @@ Selection::remove (TimeAxisView* track)
}
}
void
Selection::remove (ControlPoint* p)
{
PointSelection::iterator i = find (points.begin(), points.end(), p);
if (i != points.end ()) {
points.erase (i);
}
}
void
Selection::remove (const TrackViewList& track_list)
{
@ -879,17 +890,22 @@ void
Selection::toggle (ControlPoint* cp)
{
cp->set_selected (!cp->get_selected ());
set_point_selection_from_line (cp->line ());
PointSelection::iterator i = find (points.begin(), points.end(), cp);
if (i == points.end()) {
points.push_back (cp);
} else {
points.erase (i);
}
PointsChanged (); /* EMIT SIGNAL */
}
void
Selection::toggle (vector<ControlPoint*> const & cps)
{
for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
(*i)->set_selected (!(*i)->get_selected ());
toggle (*i);
}
set_point_selection_from_line (cps.front()->line ());
}
void
@ -935,6 +951,13 @@ Selection::set (list<Selectable*> const & selectables)
add (selectables);
}
void
Selection::add (PointSelection const & s)
{
for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
points.push_back (*i);
}
}
void
Selection::add (list<Selectable*> const & selectables)
@ -979,17 +1002,16 @@ void
Selection::add (ControlPoint* cp)
{
cp->set_selected (true);
set_point_selection_from_line (cp->line ());
points.push_back (cp);
PointsChanged (); /* EMIT SIGNAL */
}
void
Selection::add (vector<ControlPoint*> const & cps)
{
for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
(*i)->set_selected (true);
add (*i);
}
set_point_selection_from_line (cps.front()->line ());
}
void
@ -999,18 +1021,11 @@ Selection::set (ControlPoint* cp)
return;
}
/* We're going to set up the PointSelection from the selected ControlPoints
on this point's line, so we need to deselect all ControlPoints before
we re-add this one.
*/
for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
cp->line().nth (i)->set_selected (false);
}
vector<ControlPoint*> cps;
cps.push_back (cp);
add (cps);
add (cp);
}
void
@ -1083,73 +1098,6 @@ MarkerSelection::range (framepos_t& s, framepos_t& e)
e = std::max (s, e);
}
/** Automation control point selection is mostly manipulated using the selected state
* of the ControlPoints themselves. For example, to add a point to a selection, its
* ControlPoint is marked as selected and then this method is called. It sets up
* our PointSelection from the selected ControlPoints of a given AutomationLine.
*
* We can't use ControlPoints directly in the selection, as we need to express a
* selection of not just a visible ControlPoint but also (possibly) some invisible
* points nearby. Hence the selection stores AutomationRanges, and these are synced
* with ControlPoint selection state using AutomationLine::set_selected_points.
*/
void
Selection::set_point_selection_from_line (AutomationLine const & line)
{
points.clear ();
AutomationRange current (DBL_MAX, 0, 1, 0, &line.trackview);
for (uint32_t i = 0; i < line.npoints(); ++i) {
ControlPoint const * cp = line.nth (i);
if (cp->get_selected()) {
/* x and y position of this control point in coordinates suitable for
an AutomationRange (ie model time and fraction of track height)
*/
double const x = (*(cp->model()))->when;
double const y = 1 - (cp->get_y() / line.trackview.current_height ());
/* work out the position of a rectangle the size of a control point centred
on this point
*/
double const size = cp->size ();
double const x_size = line.time_converter().from (line.trackview.editor().pixel_to_frame (size));
double const y_size = size / line.trackview.current_height ();
double const x1 = max (0.0, x - x_size / 2);
double const x2 = x + x_size / 2;
double const y1 = max (0.0, y - y_size / 2);
double const y2 = y + y_size / 2;
/* extend the current AutomationRange to put this point in */
current.start = min (current.start, x1);
current.end = max (current.end, x2);
current.low_fract = min (current.low_fract, y1);
current.high_fract = max (current.high_fract, y2);
} else {
/* this point isn't selected; if the current AutomationRange has some
stuff in it, push it onto the list and make a new one
*/
if (current.start < DBL_MAX) {
points.push_back (current);
current = AutomationRange (DBL_MAX, 0, 1, 0, &line.trackview);
}
}
}
/* Maybe push the current AutomationRange, as above */
if (current.start < DBL_MAX) {
points.push_back (current);
current = AutomationRange (DBL_MAX, 0, 1, 0, &line.trackview);
}
PointsChanged (); /* EMIT SIGNAL */
}
XMLNode&
Selection::get_state () const
{

View File

@ -165,6 +165,7 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList
void add (Marker*);
void add (const std::list<Marker*>&);
void add (const RegionSelection&);
void add (const PointSelection&);
void remove (TimeAxisView*);
void remove (const TrackViewList&);
void remove (const MidiNoteSelection&);
@ -178,6 +179,7 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList
void remove (const std::list<boost::shared_ptr<ARDOUR::Playlist> >&);
void remove (const std::list<Selectable*>&);
void remove (Marker*);
void remove (ControlPoint *);
void remove_regions (TimeAxisView *);
@ -202,8 +204,6 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList
int set_state (XMLNode const &, int);
private:
void set_point_selection_from_line (AutomationLine const &);
PublicEditor const * editor;
uint32_t next_time_id;
bool _no_tracks_changed;

View File

@ -32,6 +32,8 @@
#include "time_info_box.h"
#include "audio_clock.h"
#include "editor.h"
#include "control_point.h"
#include "automation_line.h"
#include "i18n.h"
@ -267,8 +269,9 @@ TimeInfoBox::selection_changed ()
s = max_framepos;
e = 0;
for (PointSelection::iterator i = selection.points.begin(); i != selection.points.end(); ++i) {
s = min (s, (framepos_t) i->start);
e = max (e, (framepos_t) i->end);
framepos_t const p = (*i)->line().session_position ((*i)->model ());
s = min (s, p);
e = max (e, p);
}
selection_start->set_off (false);
selection_end->set_off (false);

View File

@ -123,7 +123,6 @@ public:
void fast_simple_add (double when, double value);
void merge_nascent (double when);
void reset_range (double start, double end);
void erase_range (double start, double end);
void erase (iterator);
void erase (iterator, iterator);

View File

@ -557,37 +557,6 @@ ControlList::erase (double when, double value)
maybe_signal_changed ();
}
void
ControlList::reset_range (double start, double endt)
{
bool reset = false;
{
Glib::Mutex::Lock lm (_lock);
ControlEvent cp (start, 0.0f);
iterator s;
iterator e;
if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) {
cp.when = endt;
e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
for (iterator i = s; i != e; ++i) {
(*i)->value = _default_value;
}
reset = true;
mark_dirty ();
}
}
if (reset) {
maybe_signal_changed ();
}
}
void
ControlList::erase_range (double start, double endt)
{