Allow drags of automation in time ranges where the automation is on a MIDI track...
authorCarl Hetherington <carl@carlh.net>
Thu, 9 Sep 2010 21:35:28 +0000 (21:35 +0000)
committerCarl Hetherington <carl@carlh.net>
Thu, 9 Sep 2010 21:35:28 +0000 (21:35 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7765 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/automation_line.cc
gtk2_ardour/automation_line.h
gtk2_ardour/automation_time_axis.cc
gtk2_ardour/automation_time_axis.h
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_drag.h

index 1b20d4c2871657e4d0e73b56e10076c0fe0db3b2..880f0aa3e89a9ee51fb9d46299ddd4d43d4b3b17 100644 (file)
@@ -806,7 +806,6 @@ AutomationLine::end_drag ()
                new MementoCommand<AutomationList>(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<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;
+}
index ec6034ccf2e7b9fa902bc8c8a9a4ca723a2cd6c5..fdace9a6e66f2e862fc2d19460b1cb821b236c85 100644 (file)
@@ -138,6 +138,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
                return _time_converter;
        }
 
+       std::pair<ARDOUR::framepos_t, ARDOUR::framepos_t> get_point_x_range () const;
+
        void set_maximum_time (ARDOUR::framepos_t);
        ARDOUR::framepos_t maximum_time () const {
                return _maximum_time;
index b356ce75958147393117e8aa1a4c5ef764168cc4..760794dd1eddb68574ccca5926f61603db6244c8 100644 (file)
@@ -1044,3 +1044,17 @@ AutomationTimeAxisView::has_automation () const
 {
        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;
+}
index 55fcb142026fd54229413564c5874d20d5b93870..798a144cabcce87f308d49046390408e24311f1f 100644 (file)
@@ -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<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 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);
index e836877be8f2a17077ef16b3bc279e23233ed101..ae04ad3224501cee06df54f33eae4034f7421209 100644 (file)
@@ -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<AudioRange> const & r)
-       : Drag (e, i)
+AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> 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<AutomationTimeAxisView*> (_item->get_data ("trackview"));
        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
@@ -3757,67 +3792,120 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* 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) {
+               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<Line>::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<AutomationList> the_list = _line->the_list ();
-               for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
+               for (list<AudioRange>::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;
+                       framecnt_t const half = (i->start + i->end) / 2;
+                       
+                       /* find the line that this audio range starts in */
+                       list<Line>::iterator j = _lines.begin();
+                       while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
+                               ++j;
                        }
-                       framepos_t b = j->end - 64;
-                       if (b < h) {
-                               b = h;
+
+                       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 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 */
                        
-                       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);
+                       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);
+                       }
                }
 
-               uint32_t const N = _line->npoints ();
-               for (uint32_t i = 0; i < N; ++i) {
+               _nothing_to_drag = true;
 
-                       ControlPoint* p = _line->nth (i);
+               /* Find all the points that should be dragged and put them in the relevant
+                  points lists in the Line structs.
+               */
 
-                       list<AudioRange>::const_iterator j = _ranges.begin ();
-                       while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
-                               ++j;
-                       }
+               for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
 
-                       if (j != _ranges.end()) {
-                               points.push_back (p);
+                       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()) {
-               _nothing_to_drag = true;
+       if (_nothing_to_drag) {
                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
@@ -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<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 */
+               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<Line>::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<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
+               i->line->clear_always_in_view ();
+               i->line->reset ();
+       }
 }
 
 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
index 6b4420715f26bc0abbae2ca64f4dc7596fe68f88..80ffbb927c01d1e7b9402b71fecaf574af56ddb8 100644 (file)
@@ -826,7 +826,17 @@ public:
 private:
        std::list<ARDOUR::AudioRange> _ranges;
        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;
 };