Allow drags of automation in time ranges where the automation is on a MIDI track and may span different regions. Fixes #3366.
git-svn-id: svn://localhost/ardour2/branches/3.0@7765 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
5f27e70016
commit
86244875a4
@ -806,7 +806,6 @@ AutomationLine::end_drag ()
|
|||||||
new MementoCommand<AutomationList>(memento_command_binder (), 0, &alist->get_state())
|
new MementoCommand<AutomationList>(memento_command_binder (), 0, &alist->get_state())
|
||||||
);
|
);
|
||||||
|
|
||||||
trackview.editor().session()->commit_reversible_command ();
|
|
||||||
trackview.editor().session()->set_dirty ();
|
trackview.editor().session()->set_dirty ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1338,3 +1337,18 @@ AutomationLine::set_maximum_time (framepos_t t)
|
|||||||
{
|
{
|
||||||
_maximum_time = t;
|
_maximum_time = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @return min and max x positions of points that are in the list, in session frames */
|
||||||
|
pair<framepos_t, framepos_t>
|
||||||
|
AutomationLine::get_point_x_range () const
|
||||||
|
{
|
||||||
|
pair<framepos_t, framepos_t> r (max_frames, 0);
|
||||||
|
|
||||||
|
for (AutomationList::const_iterator i = the_list()->begin(); i != the_list()->end(); ++i) {
|
||||||
|
r.first = min (r.first, _time_converter.to ((*i)->when) + _time_converter.origin_b ());
|
||||||
|
r.second = max (r.second, _time_converter.to ((*i)->when) + _time_converter.origin_b ());
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
@ -138,6 +138,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
|
|||||||
return _time_converter;
|
return _time_converter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<ARDOUR::framepos_t, ARDOUR::framepos_t> get_point_x_range () const;
|
||||||
|
|
||||||
void set_maximum_time (ARDOUR::framepos_t);
|
void set_maximum_time (ARDOUR::framepos_t);
|
||||||
ARDOUR::framepos_t maximum_time () const {
|
ARDOUR::framepos_t maximum_time () const {
|
||||||
return _maximum_time;
|
return _maximum_time;
|
||||||
|
@ -1044,3 +1044,17 @@ AutomationTimeAxisView::has_automation () const
|
|||||||
{
|
{
|
||||||
return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
|
return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list<boost::shared_ptr<AutomationLine> >
|
||||||
|
AutomationTimeAxisView::lines () const
|
||||||
|
{
|
||||||
|
list<boost::shared_ptr<AutomationLine> > lines;
|
||||||
|
|
||||||
|
if (_line) {
|
||||||
|
lines.push_back (_line);
|
||||||
|
} else if (_view) {
|
||||||
|
lines = _view->get_lines ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
@ -75,8 +75,13 @@ class AutomationTimeAxisView : public TimeAxisView {
|
|||||||
void add_automation_event (ArdourCanvas::Item *item, GdkEvent *event, nframes_t, double);
|
void add_automation_event (ArdourCanvas::Item *item, GdkEvent *event, nframes_t, double);
|
||||||
|
|
||||||
void clear_lines ();
|
void clear_lines ();
|
||||||
|
|
||||||
|
/** @return Our AutomationLine, if this view has one, or 0 if it uses AutomationRegionViews */
|
||||||
boost::shared_ptr<AutomationLine> line() { return _line; }
|
boost::shared_ptr<AutomationLine> line() { return _line; }
|
||||||
|
|
||||||
|
/** @return All AutomationLines associated with this view */
|
||||||
|
std::list<boost::shared_ptr<AutomationLine> > lines () const;
|
||||||
|
|
||||||
void set_selected_points (PointSelection&);
|
void set_selected_points (PointSelection&);
|
||||||
void get_selectables (ARDOUR::framepos_t start, ARDOUR::framepos_t end, double top, double bot, std::list<Selectable *>&);
|
void get_selectables (ARDOUR::framepos_t start, ARDOUR::framepos_t end, double top, double bot, std::list<Selectable *>&);
|
||||||
void get_inverted_selectables (Selection&, std::list<Selectable*>& results);
|
void get_inverted_selectables (Selection&, std::list<Selectable*>& results);
|
||||||
|
@ -2649,7 +2649,9 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
|
|||||||
} else {
|
} else {
|
||||||
motion (event, false);
|
motion (event, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_point->line().end_drag ();
|
_point->line().end_drag ();
|
||||||
|
_editor->session()->commit_reversible_command ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -2759,6 +2761,7 @@ LineDrag::finished (GdkEvent* event, bool)
|
|||||||
{
|
{
|
||||||
motion (event, false);
|
motion (event, false);
|
||||||
_line->end_drag ();
|
_line->end_drag ();
|
||||||
|
_editor->session()->commit_reversible_command ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -3739,8 +3742,8 @@ NoteDrag::aborted ()
|
|||||||
/* XXX: TODO */
|
/* XXX: TODO */
|
||||||
}
|
}
|
||||||
|
|
||||||
AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
|
AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
|
||||||
: Drag (e, i)
|
: Drag (editor, item)
|
||||||
, _ranges (r)
|
, _ranges (r)
|
||||||
, _nothing_to_drag (false)
|
, _nothing_to_drag (false)
|
||||||
{
|
{
|
||||||
@ -3749,7 +3752,39 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list
|
|||||||
_atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
|
_atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
|
||||||
assert (_atav);
|
assert (_atav);
|
||||||
|
|
||||||
_line = _atav->line ();
|
/* get all lines in the automation view */
|
||||||
|
list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
|
||||||
|
|
||||||
|
/* find those that overlap the ranges being dragged */
|
||||||
|
list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
|
||||||
|
while (i != lines.end ()) {
|
||||||
|
list<boost::shared_ptr<AutomationLine> >::iterator j = i;
|
||||||
|
++j;
|
||||||
|
|
||||||
|
pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
|
||||||
|
|
||||||
|
/* check this range against all the AudioRanges that we are using */
|
||||||
|
list<AudioRange>::const_iterator k = _ranges.begin ();
|
||||||
|
while (k != _ranges.end()) {
|
||||||
|
if (k->coverage (r.first, r.second) != OverlapNone) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add it to our list if it overlaps at all */
|
||||||
|
if (k != _ranges.end()) {
|
||||||
|
Line n;
|
||||||
|
n.line = *i;
|
||||||
|
n.state = 0;
|
||||||
|
n.range = r;
|
||||||
|
_lines.push_back (n);
|
||||||
|
}
|
||||||
|
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now ::lines contains the AutomationLines that somehow overlap our drag */
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -3757,67 +3792,120 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|||||||
{
|
{
|
||||||
Drag::start_grab (event, cursor);
|
Drag::start_grab (event, cursor);
|
||||||
|
|
||||||
list<ControlPoint*> points;
|
/* Get line states before we start changing things */
|
||||||
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||||
XMLNode* state = &_line->get_state ();
|
i->state = &i->line->get_state ();
|
||||||
|
}
|
||||||
|
|
||||||
if (_ranges.empty()) {
|
if (_ranges.empty()) {
|
||||||
|
|
||||||
uint32_t const N = _line->npoints ();
|
/* No selected time ranges: drag all points */
|
||||||
for (uint32_t i = 0; i < N; ++i) {
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||||
points.push_back (_line->nth (i));
|
uint32_t const N = i->line->npoints ();
|
||||||
|
for (uint32_t j = 0; j < N; ++j) {
|
||||||
|
i->points.push_back (i->line->nth (j));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
boost::shared_ptr<AutomationList> the_list = _line->the_list ();
|
for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
|
||||||
for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
|
|
||||||
|
|
||||||
/* fade into and out of the region that we're dragging;
|
framecnt_t const half = (i->start + i->end) / 2;
|
||||||
64 samples length plucked out of thin air.
|
|
||||||
*/
|
|
||||||
framecnt_t const h = (j->start + j->end) / 2;
|
|
||||||
framepos_t a = j->start + 64;
|
|
||||||
if (a > h) {
|
|
||||||
a = h;
|
|
||||||
}
|
|
||||||
framepos_t b = j->end - 64;
|
|
||||||
if (b < h) {
|
|
||||||
b = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
the_list->add (j->start, the_list->eval (j->start));
|
/* find the line that this audio range starts in */
|
||||||
_line->add_always_in_view (j->start);
|
list<Line>::iterator j = _lines.begin();
|
||||||
the_list->add (a, the_list->eval (a));
|
while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
|
||||||
_line->add_always_in_view (a);
|
|
||||||
the_list->add (b, the_list->eval (b));
|
|
||||||
_line->add_always_in_view (b);
|
|
||||||
the_list->add (j->end, the_list->eval (j->end));
|
|
||||||
_line->add_always_in_view (j->end);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t const N = _line->npoints ();
|
|
||||||
for (uint32_t i = 0; i < N; ++i) {
|
|
||||||
|
|
||||||
ControlPoint* p = _line->nth (i);
|
|
||||||
|
|
||||||
list<AudioRange>::const_iterator j = _ranges.begin ();
|
|
||||||
while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
|
|
||||||
++j;
|
++j;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j != _ranges.end()) {
|
if (j != _lines.end()) {
|
||||||
points.push_back (p);
|
boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
|
||||||
|
|
||||||
|
/* j is the line that this audio range starts in; fade into it;
|
||||||
|
64 samples length plucked out of thin air.
|
||||||
|
*/
|
||||||
|
|
||||||
|
framepos_t a = i->start + 64;
|
||||||
|
if (a > half) {
|
||||||
|
a = half;
|
||||||
|
}
|
||||||
|
|
||||||
|
double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
|
||||||
|
double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
|
||||||
|
|
||||||
|
the_list->add (p, the_list->eval (p));
|
||||||
|
j->line->add_always_in_view (p);
|
||||||
|
the_list->add (q, the_list->eval (q));
|
||||||
|
j->line->add_always_in_view (q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* same thing for the end */
|
||||||
|
|
||||||
|
j = _lines.begin();
|
||||||
|
while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j != _lines.end()) {
|
||||||
|
boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
|
||||||
|
|
||||||
|
/* j is the line that this audio range starts in; fade out of it;
|
||||||
|
64 samples length plucked out of thin air.
|
||||||
|
*/
|
||||||
|
|
||||||
|
framepos_t b = i->end - 64;
|
||||||
|
if (b < half) {
|
||||||
|
b = half;
|
||||||
|
}
|
||||||
|
|
||||||
|
double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
|
||||||
|
double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
|
||||||
|
|
||||||
|
the_list->add (p, the_list->eval (p));
|
||||||
|
j->line->add_always_in_view (p);
|
||||||
|
the_list->add (q, the_list->eval (q));
|
||||||
|
j->line->add_always_in_view (q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_nothing_to_drag = true;
|
||||||
|
|
||||||
|
/* Find all the points that should be dragged and put them in the relevant
|
||||||
|
points lists in the Line structs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||||
|
|
||||||
|
uint32_t const N = i->line->npoints ();
|
||||||
|
for (uint32_t j = 0; j < N; ++j) {
|
||||||
|
|
||||||
|
/* here's a control point on this line */
|
||||||
|
ControlPoint* p = i->line->nth (j);
|
||||||
|
double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
|
||||||
|
|
||||||
|
/* see if it's inside a range */
|
||||||
|
list<AudioRange>::const_iterator k = _ranges.begin ();
|
||||||
|
while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
|
||||||
|
++k;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k != _ranges.end()) {
|
||||||
|
/* dragging this point */
|
||||||
|
_nothing_to_drag = false;
|
||||||
|
i->points.push_back (p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (points.empty()) {
|
if (_nothing_to_drag) {
|
||||||
_nothing_to_drag = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||||
|
i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -3827,10 +3915,12 @@ AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float const f = 1 - (_drags->current_pointer_y() / _line->height());
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||||
|
float const f = 1 - (_drags->current_pointer_y() / i->line->height());
|
||||||
|
|
||||||
/* we are ignoring x position for this drag, so we can just pass in anything */
|
/* we are ignoring x position for this drag, so we can just pass in anything */
|
||||||
_line->drag_motion (0, f, true, false);
|
i->line->drag_motion (0, f, true, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -3841,15 +3931,21 @@ AutomationRangeDrag::finished (GdkEvent* event, bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
motion (event, false);
|
motion (event, false);
|
||||||
_line->end_drag ();
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||||
_line->clear_always_in_view ();
|
i->line->end_drag ();
|
||||||
|
i->line->clear_always_in_view ();
|
||||||
|
}
|
||||||
|
|
||||||
|
_editor->session()->commit_reversible_command ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AutomationRangeDrag::aborted ()
|
AutomationRangeDrag::aborted ()
|
||||||
{
|
{
|
||||||
_line->clear_always_in_view ();
|
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||||
_line->reset ();
|
i->line->clear_always_in_view ();
|
||||||
|
i->line->reset ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
|
DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
|
||||||
|
@ -826,7 +826,17 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::list<ARDOUR::AudioRange> _ranges;
|
std::list<ARDOUR::AudioRange> _ranges;
|
||||||
AutomationTimeAxisView* _atav;
|
AutomationTimeAxisView* _atav;
|
||||||
boost::shared_ptr<AutomationLine> _line;
|
|
||||||
|
/** A line that is part of the drag */
|
||||||
|
struct Line {
|
||||||
|
boost::shared_ptr<AutomationLine> line; ///< the line
|
||||||
|
std::list<ControlPoint*> points; ///< points to drag on the line
|
||||||
|
std::pair<ARDOUR::framepos_t, ARDOUR::framepos_t> range; ///< the range of all points on the line, in session frames
|
||||||
|
XMLNode* state; ///< the XML state node before the drag
|
||||||
|
};
|
||||||
|
|
||||||
|
std::list<Line> _lines;
|
||||||
|
|
||||||
bool _nothing_to_drag;
|
bool _nothing_to_drag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user