From 86244875a40206694c142af8fe1128f28293c467 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 9 Sep 2010 21:35:28 +0000 Subject: [PATCH] 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 --- gtk2_ardour/automation_line.cc | 16 ++- gtk2_ardour/automation_line.h | 2 + gtk2_ardour/automation_time_axis.cc | 14 ++ gtk2_ardour/automation_time_axis.h | 5 + gtk2_ardour/editor_drag.cc | 204 ++++++++++++++++++++-------- gtk2_ardour/editor_drag.h | 12 +- 6 files changed, 197 insertions(+), 56 deletions(-) diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 1b20d4c287..880f0aa3e8 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -806,7 +806,6 @@ AutomationLine::end_drag () new MementoCommand(memento_command_binder (), 0, &alist->get_state()) ); - trackview.editor().session()->commit_reversible_command (); trackview.editor().session()->set_dirty (); } @@ -1338,3 +1337,18 @@ AutomationLine::set_maximum_time (framepos_t t) { _maximum_time = t; } + + +/** @return min and max x positions of points that are in the list, in session frames */ +pair +AutomationLine::get_point_x_range () const +{ + pair 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; +} diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index ec6034ccf2..fdace9a6e6 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -138,6 +138,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible return _time_converter; } + std::pair get_point_x_range () const; + void set_maximum_time (ARDOUR::framepos_t); ARDOUR::framepos_t maximum_time () const { return _maximum_time; diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index b356ce7595..760794dd1e 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -1044,3 +1044,17 @@ AutomationTimeAxisView::has_automation () const { return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) ); } + +list > +AutomationTimeAxisView::lines () const +{ + list > lines; + + if (_line) { + lines.push_back (_line); + } else if (_view) { + lines = _view->get_lines (); + } + + return lines; +} diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h index 55fcb14202..798a144cab 100644 --- a/gtk2_ardour/automation_time_axis.h +++ b/gtk2_ardour/automation_time_axis.h @@ -75,8 +75,13 @@ class AutomationTimeAxisView : public TimeAxisView { void add_automation_event (ArdourCanvas::Item *item, GdkEvent *event, nframes_t, double); void clear_lines (); + + /** @return Our AutomationLine, if this view has one, or 0 if it uses AutomationRegionViews */ boost::shared_ptr line() { return _line; } + /** @return All AutomationLines associated with this view */ + std::list > lines () const; + void set_selected_points (PointSelection&); void get_selectables (ARDOUR::framepos_t start, ARDOUR::framepos_t end, double top, double bot, std::list&); void get_inverted_selectables (Selection&, std::list& results); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index e836877be8..ae04ad3224 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -2649,7 +2649,9 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred) } else { motion (event, false); } + _point->line().end_drag (); + _editor->session()->commit_reversible_command (); } void @@ -2759,6 +2761,7 @@ LineDrag::finished (GdkEvent* event, bool) { motion (event, false); _line->end_drag (); + _editor->session()->commit_reversible_command (); } void @@ -3739,8 +3742,8 @@ NoteDrag::aborted () /* XXX: TODO */ } -AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list const & r) - : Drag (e, i) +AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list const & r) + : Drag (editor, item) , _ranges (r) , _nothing_to_drag (false) { @@ -3749,7 +3752,39 @@ AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list _atav = reinterpret_cast (_item->get_data ("trackview")); assert (_atav); - _line = _atav->line (); + /* get all lines in the automation view */ + list > lines = _atav->lines (); + + /* find those that overlap the ranges being dragged */ + list >::iterator i = lines.begin (); + while (i != lines.end ()) { + list >::iterator j = i; + ++j; + + pair const r = (*i)->get_point_x_range (); + + /* check this range against all the AudioRanges that we are using */ + list::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 @@ -3757,67 +3792,120 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) { Drag::start_grab (event, cursor); - list points; + /* Get line states before we start changing things */ + for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { + i->state = &i->line->get_state (); + } - XMLNode* state = &_line->get_state (); - if (_ranges.empty()) { - - uint32_t const N = _line->npoints (); - for (uint32_t i = 0; i < N; ++i) { - points.push_back (_line->nth (i)); + + /* No selected time ranges: drag all points */ + for (list::iterator i = _lines.begin(); i != _lines.end(); ++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 { - boost::shared_ptr the_list = _line->the_list (); - for (list::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) { + for (list::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) { - /* fade into and out of the region that we're dragging; - 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; - } + framecnt_t const half = (i->start + i->end) / 2; - the_list->add (j->start, the_list->eval (j->start)); - _line->add_always_in_view (j->start); - the_list->add (a, the_list->eval (a)); - _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::const_iterator j = _ranges.begin (); - while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) { + /* find the line that this audio range starts in */ + list::iterator j = _lines.begin(); + while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) { ++j; } - if (j != _ranges.end()) { - points.push_back (p); + if (j != _lines.end()) { + boost::shared_ptr 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 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::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::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()) { - _nothing_to_drag = true; + if (_nothing_to_drag) { return; } - _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state); + for (list::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 @@ -3826,11 +3914,13 @@ AutomationRangeDrag::motion (GdkEvent* event, bool first_move) if (_nothing_to_drag) { return; } - - float const f = 1 - (_drags->current_pointer_y() / _line->height()); - /* we are ignoring x position for this drag, so we can just pass in anything */ - _line->drag_motion (0, f, true, false); + for (list::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 */ + i->line->drag_motion (0, f, true, false); + } } void @@ -3841,15 +3931,21 @@ AutomationRangeDrag::finished (GdkEvent* event, bool) } motion (event, false); - _line->end_drag (); - _line->clear_always_in_view (); + for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { + i->line->end_drag (); + i->line->clear_always_in_view (); + } + + _editor->session()->commit_reversible_command (); } void AutomationRangeDrag::aborted () { - _line->clear_always_in_view (); - _line->reset (); + for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { + i->line->clear_always_in_view (); + i->line->reset (); + } } DraggingView::DraggingView (RegionView* v, RegionDrag* parent) diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index 6b4420715f..80ffbb927c 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -826,7 +826,17 @@ public: private: std::list _ranges; AutomationTimeAxisView* _atav; - boost::shared_ptr _line; + + /** A line that is part of the drag */ + struct Line { + boost::shared_ptr line; ///< the line + std::list points; ///< points to drag on the line + std::pair range; ///< the range of all points on the line, in session frames + XMLNode* state; ///< the XML state node before the drag + }; + + std::list _lines; + bool _nothing_to_drag; };