2 Copyright (C) 2002-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "pbd/stl_delete.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/stacktrace.h"
29 #include "ardour/automation_list.h"
30 #include "ardour/dB.h"
31 #include "evoral/Curve.hpp"
33 #include "simplerect.h"
34 #include "automation_line.h"
35 #include "control_point.h"
36 #include "rgb_macros.h"
37 #include "ardour_ui.h"
38 #include "public_editor.h"
40 #include "selection.h"
41 #include "time_axis_view.h"
42 #include "point_selection.h"
43 #include "automation_selectable.h"
44 #include "automation_time_axis.h"
45 #include "public_editor.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/session.h"
54 using namespace ARDOUR;
56 using namespace Editing;
57 using namespace Gnome; // for Canvas
59 static const Evoral::IdentityConverter<double, sframes_t> default_converter;
61 AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent,
62 boost::shared_ptr<AutomationList> al,
63 const Evoral::TimeConverter<double, sframes_t>* converter)
67 , _parent_group (parent)
68 , _time_converter (converter ? (*converter) : default_converter)
70 _interpolation = al->interpolation();
71 points_visible = false;
72 update_pending = false;
73 _uses_gain_mapping = false;
76 terminal_points_can_slide = true;
79 group = new ArdourCanvas::Group (parent);
80 group->property_x() = 0.0;
81 group->property_y() = 0.0;
83 line = new ArdourCanvas::Line (*group);
84 line->property_width_pixels() = (guint)1;
85 line->set_data ("line", this);
87 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
89 alist->StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
91 trackview.session().register_with_memento_command_factory(alist->id(), this);
93 if (alist->parameter().type() == GainAutomation ||
94 alist->parameter().type() == EnvelopeAutomation) {
95 set_uses_gain_mapping (true);
98 set_interpolation(alist->interpolation());
101 AutomationLine::~AutomationLine ()
103 vector_delete (&control_points);
108 AutomationLine::event_handler (GdkEvent* event)
110 return PublicEditor::instance().canvas_line_event (event, line, this);
114 AutomationLine::queue_reset ()
116 if (!update_pending) {
117 update_pending = true;
118 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
123 AutomationLine::show ()
125 if (_interpolation != AutomationList::Discrete) {
129 if (points_visible) {
130 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
139 AutomationLine::hide ()
142 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
149 AutomationLine::control_point_box_size ()
151 if (_interpolation == AutomationList::Discrete) {
152 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
156 if (_height > TimeAxisView::hLarger) {
158 } else if (_height > (guint32) TimeAxisView::hNormal) {
165 AutomationLine::set_height (guint32 h)
170 double bsz = control_point_box_size();
172 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
173 (*i)->set_size (bsz);
181 AutomationLine::set_line_color (uint32_t color)
184 line->property_fill_color_rgba() = color;
188 AutomationLine::set_uses_gain_mapping (bool yn)
190 if (yn != _uses_gain_mapping) {
191 _uses_gain_mapping = yn;
197 AutomationLine::nth (uint32_t n)
199 if (n < control_points.size()) {
200 return control_points[n];
207 AutomationLine::modify_point_y (ControlPoint& cp, double y)
209 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
210 and needs to be converted to a canvas unit distance.
215 y = _height - (y * _height);
217 double const x = trackview.editor().frame_to_unit (_time_converter.to((*cp.model())->when));
219 trackview.editor().current_session()->begin_reversible_command (_("automation event move"));
220 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
222 cp.move_to (x, y, ControlPoint::Full);
223 reset_line_coords (cp);
225 if (line_points.size() > 1) {
226 line->property_points() = line_points;
230 sync_model_with_view_point (cp, false, 0);
233 update_pending = false;
235 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
236 trackview.editor().current_session()->commit_reversible_command ();
237 trackview.editor().current_session()->set_dirty ();
242 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
245 uint32_t last_movable = UINT_MAX;
246 double x_limit = DBL_MAX;
248 /* this just changes the current view. it does not alter
249 the model in any way at all.
252 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
253 and needs to be converted to a canvas unit distance.
258 y = _height - (y * _height);
260 if (cp.can_slide()) {
262 /* x-coord cannot move beyond adjacent points or the start/end, and is
263 already in frames. it needs to be converted to canvas units.
266 x = trackview.editor().frame_to_unit (x);
268 /* clamp x position using view coordinates */
270 ControlPoint *before;
273 if (cp.view_index()) {
274 before = nth (cp.view_index() - 1);
275 x = max (x, before->get_x()+1.0);
282 if (cp.view_index() < control_points.size() - 1) {
284 after = nth (cp.view_index() + 1);
286 /*if it is a "spike" leave the x alone */
288 if (after->get_x() - before->get_x() < 2) {
292 x = min (x, after->get_x()-1.0);
302 /* find the first point that can't move */
304 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
305 if (!after->can_slide()) {
306 x_limit = after->get_x() - 1.0;
307 last_movable = after->view_index();
312 delta = x - cp.get_x();
317 /* leave the x-coordinate alone */
319 x = trackview.editor().frame_to_unit (_time_converter.to((*cp.model())->when));
325 cp.move_to (x, y, ControlPoint::Full);
326 reset_line_coords (cp);
330 uint32_t limit = min (control_points.size(), (size_t)last_movable);
332 /* move the current point to wherever the user told it to go, subject
336 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
337 reset_line_coords (cp);
339 /* now move all subsequent control points, to reflect the motion.
342 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
343 ControlPoint *p = nth (i);
346 if (p->can_slide()) {
347 new_x = min (p->get_x() + delta, x_limit);
348 p->move_to (new_x, p->get_y(), ControlPoint::Full);
349 reset_line_coords (*p);
356 AutomationLine::reset_line_coords (ControlPoint& cp)
358 if (cp.view_index() < line_points.size()) {
359 line_points[cp.view_index()].set_x (cp.get_x());
360 line_points[cp.view_index()].set_y (cp.get_y());
365 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
369 update_pending = true;
371 for (uint32_t i = start; i <= end; ++i) {
373 sync_model_with_view_point (*p, false, 0);
378 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
380 /* part one: find out where the visual control point is.
381 initial results are in canvas units. ask the
382 line to convert them to something relevant.
385 mr.xval = cp.get_x();
386 mr.yval = 1.0 - (cp.get_y() / _height);
388 /* if xval has not changed, set it directly from the model to avoid rounding errors */
390 if (mr.xval == trackview.editor().frame_to_unit(_time_converter.to((*cp.model())->when))) {
391 mr.xval = (*cp.model())->when;
393 mr.xval = trackview.editor().unit_to_frame (mr.xval);
396 /* convert to model units
399 view_to_model_coord (mr.xval, mr.yval);
401 /* part 2: find out where the model point is now
404 mr.xpos = (*cp.model())->when;
405 mr.ypos = (*cp.model())->value;
407 /* part 3: get the position of the visual control
408 points before and after us.
411 ControlPoint* before;
414 if (cp.view_index()) {
415 before = nth (cp.view_index() - 1);
420 after = nth (cp.view_index() + 1);
423 mr.xmin = (*before->model())->when;
424 mr.ymin = (*before->model())->value;
425 mr.start = before->model();
430 mr.start = cp.model();
434 mr.end = after->model();
444 AutomationLine::determine_visible_control_points (ALPoints& points)
446 uint32_t view_index, pi, n;
447 AutomationList::iterator model;
449 double last_control_point_x = 0.0;
450 double last_control_point_y = 0.0;
451 uint32_t this_rx = 0;
452 uint32_t prev_rx = 0;
453 uint32_t this_ry = 0;
454 uint32_t prev_ry = 0;
459 /* hide all existing points, and the line */
463 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
470 if (points.empty()) {
474 npoints = points.size();
476 /* compute derivative/slope for the entire line */
478 slope = new double[npoints];
480 for (n = 0; n < npoints - 1; ++n) {
481 double xdelta = points[n+1].x - points[n].x;
482 double ydelta = points[n+1].y - points[n].y;
483 slope[n] = ydelta/xdelta;
486 box_size = (uint32_t) control_point_box_size ();
488 /* read all points and decide which ones to show as control points */
492 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
494 double tx = points[pi].x;
495 double ty = points[pi].y;
497 if (isnan (tx) || isnan (ty)) {
498 warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
503 /* now ensure that the control_points vector reflects the current curve
504 state, but don't plot control points too close together. also, don't
505 plot a series of points all with the same value.
507 always plot the first and last points, of course.
510 if (invalid_point (points, pi)) {
511 /* for some reason, we are supposed to ignore this point,
512 but still keep track of the model index.
517 if (pi > 0 && pi < npoints - 1) {
518 if (slope[pi] == slope[pi-1]) {
520 /* no reason to display this point */
526 /* need to round here. the ultimate coordinates are integer
527 pixels, so tiny deltas in the coords will be eliminated
528 and we end up with "colinear" line segments. since the
529 line rendering code in libart doesn't like this very
530 much, we eliminate them here. don't do this for the first and last
534 this_rx = (uint32_t) rint (tx);
535 this_ry = (uint32_t) rint (ty);
537 if (view_index && pi != npoints && /* not the first, not the last */
538 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
539 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
540 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
544 /* ok, we should display this point */
546 if (view_index >= cpsize) {
548 /* make sure we have enough control points */
550 ControlPoint* ncp = new ControlPoint (*this);
552 ncp->set_size (box_size);
554 control_points.push_back (ncp);
558 ControlPoint::ShapeType shape;
560 if (!terminal_points_can_slide) {
562 control_points[view_index]->set_can_slide(false);
564 shape = ControlPoint::Start;
566 shape = ControlPoint::Full;
568 } else if (pi == npoints - 1) {
569 control_points[view_index]->set_can_slide(false);
570 shape = ControlPoint::End;
572 control_points[view_index]->set_can_slide(true);
573 shape = ControlPoint::Full;
576 control_points[view_index]->set_can_slide(true);
577 shape = ControlPoint::Full;
580 last_control_point_x = tx;
581 last_control_point_y = ty;
583 control_points[view_index]->reset (tx, ty, model, view_index, shape);
588 /* finally, control visibility */
590 if (_visible && points_visible) {
591 control_points[view_index]->show ();
592 control_points[view_index]->set_visible (true);
594 if (!points_visible) {
595 control_points[view_index]->set_visible (false);
602 /* discard extra CP's to avoid confusing ourselves */
604 while (control_points.size() > view_index) {
605 ControlPoint* cp = control_points.back();
606 control_points.pop_back ();
610 if (!terminal_points_can_slide) {
611 control_points.back()->set_can_slide(false);
616 if (view_index > 1) {
618 npoints = view_index;
620 /* reset the line coordinates */
622 while (line_points.size() < npoints) {
623 line_points.push_back (Art::Point (0,0));
626 while (line_points.size() > npoints) {
627 line_points.pop_back ();
630 for (view_index = 0; view_index < npoints; ++view_index) {
631 line_points[view_index].set_x (control_points[view_index]->get_x());
632 line_points[view_index].set_y (control_points[view_index]->get_y());
635 line->property_points() = line_points;
637 if (_visible && _interpolation != AutomationList::Discrete) {
643 set_selected_points (trackview.editor().get_selection().points);
648 AutomationLine::get_verbose_cursor_string (double fraction) const
650 std::string s = fraction_to_string (fraction);
651 if (_uses_gain_mapping) {
659 * @param fraction y fraction
660 * @return string representation of this value, using dB if appropriate.
663 AutomationLine::fraction_to_string (double fraction) const
667 if (_uses_gain_mapping) {
668 if (fraction == 0.0) {
669 snprintf (buf, sizeof (buf), "-inf");
671 snprintf (buf, sizeof (buf), "%.1f", coefficient_to_dB (slider_position_to_gain (fraction)));
675 view_to_model_coord (dummy, fraction);
676 if (EventTypeMap::instance().is_integer (alist->parameter())) {
677 snprintf (buf, sizeof (buf), "%d", (int)fraction);
679 snprintf (buf, sizeof (buf), "%.2f", fraction);
688 * @param s Value string in the form as returned by fraction_to_string.
689 * @return Corresponding y fraction.
692 AutomationLine::string_to_fraction (string const & s) const
699 sscanf (s.c_str(), "%lf", &v);
701 if (_uses_gain_mapping) {
702 v = gain_to_slider_position (dB_to_coefficient (v));
705 model_to_view_coord (dummy, v);
712 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
714 return p[index].x == max_frames && p[index].y == DBL_MAX;
718 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
720 p[index].x = max_frames;
721 p[index].y = DBL_MAX;
725 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
727 if (trackview.editor().current_session() == 0) { /* how? */
734 str = _("automation event move");
736 str = _("automation range drag");
739 trackview.editor().current_session()->begin_reversible_command (str);
740 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
744 first_drag_fraction = fraction;
745 last_drag_fraction = fraction;
751 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
754 drag_distance += (x - drag_x);
756 drag_distance -= (drag_x - x);
761 modify_view_point (cp, x, fraction, with_push);
763 if (line_points.size() > 1) {
764 line->property_points() = line_points;
768 did_push = with_push;
772 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
774 double ydelta = fraction - last_drag_fraction;
776 did_push = with_push;
778 last_drag_fraction = fraction;
783 //check if one of the control points on the line is in a selected range
784 bool range_found = false;
787 for (uint32_t i = i1 ; i <= i2; i++) {
789 if (cp->selected()) {
795 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
796 if ((*i)->selected()) {
797 modify_view_point (*(*i), trackview.editor().unit_to_frame ((*i)->get_x()), ((_height - (*i)->get_y()) /_height) + ydelta, with_push);
802 for (uint32_t i = i1 ; i <= i2; i++) {
804 modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
808 if (line_points.size() > 1) {
809 line->property_points() = line_points;
816 AutomationLine::end_drag (ControlPoint* cp)
825 sync_model_with_view_point (*cp, did_push, drag_distance);
827 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
832 update_pending = false;
834 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
835 trackview.editor().current_session()->commit_reversible_command ();
836 trackview.editor().current_session()->set_dirty ();
841 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
843 ModelRepresentation mr;
846 model_representation (cp, mr);
848 /* how much are we changing the central point by */
850 ydelta = mr.yval - mr.ypos;
853 apply the full change to the central point, and interpolate
854 on both axes to cover all model points represented
855 by the control point.
858 /* change all points before the primary point */
860 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
862 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
863 double y_delta = ydelta * fract;
864 double x_delta = distance * fract;
868 if (y_delta || x_delta) {
869 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
873 /* change the primary point */
875 update_pending = true;
876 alist->modify (cp.model(), mr.xval, mr.yval);
879 /* change later points */
881 AutomationList::iterator i = cp.model();
885 while (i != mr.end) {
887 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
889 /* all later points move by the same distance along the x-axis as the main point */
892 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
900 /* move all points after the range represented by the view by the same distance
901 as the main point moved.
904 alist->slide (mr.end, drag_distance);
910 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
912 ControlPoint *bcp = 0;
913 ControlPoint *acp = 0;
916 unit_xval = trackview.editor().frame_to_unit (xval);
918 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
920 if ((*i)->get_x() <= unit_xval) {
922 if (!bcp || (*i)->get_x() > bcp->get_x()) {
924 before = bcp->view_index();
927 } else if ((*i)->get_x() > unit_xval) {
929 after = acp->view_index();
938 AutomationLine::is_last_point (ControlPoint& cp)
940 ModelRepresentation mr;
942 model_representation (cp, mr);
944 // If the list is not empty, and the point is the last point in the list
946 if (!alist->empty() && mr.end == alist->end()) {
954 AutomationLine::is_first_point (ControlPoint& cp)
956 ModelRepresentation mr;
958 model_representation (cp, mr);
960 // If the list is not empty, and the point is the first point in the list
962 if (!alist->empty() && mr.start == alist->begin()) {
969 // This is copied into AudioRegionGainLine
971 AutomationLine::remove_point (ControlPoint& cp)
973 ModelRepresentation mr;
975 model_representation (cp, mr);
977 trackview.editor().current_session()->begin_reversible_command (_("remove control point"));
978 XMLNode &before = alist->get_state();
980 alist->erase (mr.start, mr.end);
982 trackview.editor().current_session()->add_command(new MementoCommand<AutomationList>(
983 *alist.get(), &before, &alist->get_state()));
984 trackview.editor().current_session()->commit_reversible_command ();
985 trackview.editor().current_session()->set_dirty ();
989 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
990 double botfrac, double topfrac, list<Selectable*>& results)
997 bool collecting = false;
999 /* Curse X11 and its inverted coordinate system! */
1001 bot = (1.0 - topfrac) * _height;
1002 top = (1.0 - botfrac) * _height;
1004 nstart = max_frames;
1007 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1008 double when = (*(*i)->model())->when;
1010 if (when >= start && when <= end) {
1012 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1015 (*i)->set_visible(true);
1017 nstart = min (nstart, when);
1018 nend = max (nend, when);
1024 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1026 nstart = max_frames;
1034 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1040 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1046 AutomationLine::set_selected_points (PointSelection& points)
1051 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1052 (*i)->set_selected(false);
1055 if (points.empty()) {
1059 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1061 if (&(*r).track != &trackview) {
1065 /* Curse X11 and its inverted coordinate system! */
1067 bot = (1.0 - (*r).high_fract) * _height;
1068 top = (1.0 - (*r).low_fract) * _height;
1070 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1072 double rstart, rend;
1074 rstart = trackview.editor().frame_to_unit ((*r).start);
1075 rend = trackview.editor().frame_to_unit ((*r).end);
1077 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1079 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1081 (*i)->set_selected(true);
1089 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1090 (*i)->show_color (false, !points_visible);
1095 void AutomationLine::set_colors() {
1096 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1097 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1098 (*i)->show_color (false, !points_visible);
1103 AutomationLine::show_selection ()
1105 TimeSelection& time (trackview.editor().get_selection().time);
1107 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1109 (*i)->set_selected(false);
1111 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1112 double rstart, rend;
1114 rstart = trackview.editor().frame_to_unit ((*r).start);
1115 rend = trackview.editor().frame_to_unit ((*r).end);
1117 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1118 (*i)->set_selected(true);
1123 (*i)->show_color (false, !points_visible);
1128 AutomationLine::hide_selection ()
1130 // show_selection ();
1134 AutomationLine::list_changed ()
1140 AutomationLine::reset_callback (const Evoral::ControlList& events)
1142 ALPoints tmp_points;
1143 uint32_t npoints = events.size();
1146 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1149 control_points.clear ();
1154 AutomationList::const_iterator ai;
1156 for (ai = events.begin(); ai != events.end(); ++ai) {
1158 double translated_x = (*ai)->when;
1159 double translated_y = (*ai)->value;
1160 model_to_view_coord (translated_x, translated_y);
1162 add_model_point (tmp_points, (*ai)->when, translated_y);
1165 determine_visible_control_points (tmp_points);
1170 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1172 tmp_points.push_back (ALPoint (trackview.editor().frame_to_unit (_time_converter.to(frame)),
1173 _height - (yfract * _height)));
1177 AutomationLine::reset ()
1179 update_pending = false;
1185 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1189 AutomationLine::clear ()
1191 /* parent must create command */
1192 XMLNode &before = get_state();
1194 trackview.editor().current_session()->add_command (
1195 new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1196 trackview.editor().current_session()->commit_reversible_command ();
1197 trackview.editor().current_session()->set_dirty ();
1201 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1206 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1213 AutomationLine::show_all_control_points ()
1215 points_visible = true;
1217 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1218 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1220 (*i)->set_visible (true);
1225 AutomationLine::hide_all_but_selected_control_points ()
1227 if (alist->interpolation() == AutomationList::Discrete) {
1231 points_visible = false;
1233 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1234 if (!(*i)->selected()) {
1235 (*i)->set_visible (false);
1241 AutomationLine::track_entered()
1243 if (alist->interpolation() != AutomationList::Discrete) {
1244 show_all_control_points();
1249 AutomationLine::track_exited()
1251 if (alist->interpolation() != AutomationList::Discrete) {
1252 hide_all_but_selected_control_points();
1257 AutomationLine::get_state (void)
1259 /* function as a proxy for the model */
1260 return alist->get_state();
1264 AutomationLine::set_state (const XMLNode &node)
1266 /* function as a proxy for the model */
1267 return alist->set_state (node);
1271 AutomationLine::view_to_model_coord (double& x, double& y) const
1273 /* TODO: This should be more generic ... */
1274 if (alist->parameter().type() == GainAutomation ||
1275 alist->parameter().type() == EnvelopeAutomation) {
1276 y = slider_position_to_gain (y);
1279 } else if (alist->parameter().type() == PanAutomation) {
1280 // vertical coordinate axis reversal
1282 } else if (alist->parameter().type() == PluginAutomation) {
1283 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1285 y = (int)(y * alist->parameter().max());
1288 x = _time_converter.from(x);
1292 AutomationLine::model_to_view_coord (double& x, double& y) const
1294 /* TODO: This should be more generic ... */
1295 if (alist->parameter().type() == GainAutomation ||
1296 alist->parameter().type() == EnvelopeAutomation) {
1297 y = gain_to_slider_position (y);
1298 } else if (alist->parameter().type() == PanAutomation) {
1299 // vertical coordinate axis reversal
1301 } else if (alist->parameter().type() == PluginAutomation) {
1302 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1304 y = y / (double)alist->parameter().max(); /* ... like this */
1307 x = _time_converter.to(x);
1312 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1314 _interpolation = style;
1316 if (style == AutomationList::Discrete) {
1317 show_all_control_points();
1320 hide_all_but_selected_control_points();