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:
parent
82c867bf2a
commit
a4434809e1
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 *>
|
||||
{
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user