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"
53 using namespace ARDOUR;
55 using namespace Editing;
56 using namespace Gnome; // for Canvas
58 static const Evoral::IdentityConverter<double, sframes_t> default_converter;
60 AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent,
61 boost::shared_ptr<AutomationList> al,
62 const Evoral::TimeConverter<double, sframes_t>* converter)
66 , _parent_group (parent)
67 , _time_converter (converter ? (*converter) : default_converter)
69 _interpolation = al->interpolation();
70 points_visible = false;
71 update_pending = false;
72 _uses_gain_mapping = false;
75 terminal_points_can_slide = true;
78 group = new ArdourCanvas::Group (parent);
79 group->property_x() = 0.0;
80 group->property_y() = 0.0;
82 line = new ArdourCanvas::Line (*group);
83 line->property_width_pixels() = (guint)1;
84 line->set_data ("line", this);
86 line->signal_event().connect (sigc::mem_fun (*this, &AutomationLine::event_handler));
88 alist->StateChanged.connect (sigc::mem_fun(*this, &AutomationLine::list_changed));
90 trackview.session()->register_with_memento_command_factory(alist->id(), this);
92 if (alist->parameter().type() == GainAutomation ||
93 alist->parameter().type() == EnvelopeAutomation) {
94 set_uses_gain_mapping (true);
97 set_interpolation(alist->interpolation());
100 AutomationLine::~AutomationLine ()
102 vector_delete (&control_points);
107 AutomationLine::event_handler (GdkEvent* event)
109 return PublicEditor::instance().canvas_line_event (event, line, this);
113 AutomationLine::queue_reset ()
115 if (!update_pending) {
116 update_pending = true;
117 Gtkmm2ext::UI::instance()->call_slot (boost::bind (&AutomationLine::reset, this));
122 AutomationLine::show ()
124 if (_interpolation != AutomationList::Discrete) {
128 if (points_visible) {
129 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
138 AutomationLine::hide ()
141 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
148 AutomationLine::control_point_box_size ()
150 if (_interpolation == AutomationList::Discrete) {
151 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
155 if (_height > TimeAxisView::hLarger) {
157 } else if (_height > (guint32) TimeAxisView::hNormal) {
164 AutomationLine::set_height (guint32 h)
169 double bsz = control_point_box_size();
171 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
172 (*i)->set_size (bsz);
180 AutomationLine::set_line_color (uint32_t color)
183 line->property_fill_color_rgba() = color;
187 AutomationLine::set_uses_gain_mapping (bool yn)
189 if (yn != _uses_gain_mapping) {
190 _uses_gain_mapping = yn;
196 AutomationLine::nth (uint32_t n)
198 if (n < control_points.size()) {
199 return control_points[n];
206 AutomationLine::modify_point_y (ControlPoint& cp, double y)
208 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
209 and needs to be converted to a canvas unit distance.
214 y = _height - (y * _height);
216 double const x = trackview.editor().frame_to_unit (_time_converter.to((*cp.model())->when));
218 trackview.editor().session()->begin_reversible_command (_("automation event move"));
219 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
221 cp.move_to (x, y, ControlPoint::Full);
222 reset_line_coords (cp);
224 if (line_points.size() > 1) {
225 line->property_points() = line_points;
229 sync_model_with_view_point (cp, false, 0);
232 update_pending = false;
234 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
235 trackview.editor().session()->commit_reversible_command ();
236 trackview.editor().session()->set_dirty ();
241 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
244 uint32_t last_movable = UINT_MAX;
245 double x_limit = DBL_MAX;
247 /* this just changes the current view. it does not alter
248 the model in any way at all.
251 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
252 and needs to be converted to a canvas unit distance.
257 y = _height - (y * _height);
259 if (cp.can_slide()) {
261 /* x-coord cannot move beyond adjacent points or the start/end, and is
262 already in frames. it needs to be converted to canvas units.
265 x = trackview.editor().frame_to_unit (x);
267 /* clamp x position using view coordinates */
269 ControlPoint *before;
272 if (cp.view_index()) {
273 before = nth (cp.view_index() - 1);
274 x = max (x, before->get_x()+1.0);
281 if (cp.view_index() < control_points.size() - 1) {
283 after = nth (cp.view_index() + 1);
285 /*if it is a "spike" leave the x alone */
287 if (after->get_x() - before->get_x() < 2) {
291 x = min (x, after->get_x()-1.0);
301 /* find the first point that can't move */
303 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
304 if (!after->can_slide()) {
305 x_limit = after->get_x() - 1.0;
306 last_movable = after->view_index();
311 delta = x - cp.get_x();
316 /* leave the x-coordinate alone */
318 x = trackview.editor().frame_to_unit (_time_converter.to((*cp.model())->when));
324 cp.move_to (x, y, ControlPoint::Full);
325 reset_line_coords (cp);
329 uint32_t limit = min (control_points.size(), (size_t)last_movable);
331 /* move the current point to wherever the user told it to go, subject
335 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
336 reset_line_coords (cp);
338 /* now move all subsequent control points, to reflect the motion.
341 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
342 ControlPoint *p = nth (i);
345 if (p->can_slide()) {
346 new_x = min (p->get_x() + delta, x_limit);
347 p->move_to (new_x, p->get_y(), ControlPoint::Full);
348 reset_line_coords (*p);
355 AutomationLine::reset_line_coords (ControlPoint& cp)
357 if (cp.view_index() < line_points.size()) {
358 line_points[cp.view_index()].set_x (cp.get_x());
359 line_points[cp.view_index()].set_y (cp.get_y());
364 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
368 update_pending = true;
370 for (uint32_t i = start; i <= end; ++i) {
372 sync_model_with_view_point (*p, false, 0);
377 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
379 /* part one: find out where the visual control point is.
380 initial results are in canvas units. ask the
381 line to convert them to something relevant.
384 mr.xval = cp.get_x();
385 mr.yval = 1.0 - (cp.get_y() / _height);
387 /* if xval has not changed, set it directly from the model to avoid rounding errors */
389 if (mr.xval == trackview.editor().frame_to_unit(_time_converter.to((*cp.model())->when))) {
390 mr.xval = (*cp.model())->when;
392 mr.xval = trackview.editor().unit_to_frame (mr.xval);
395 /* convert to model units
398 view_to_model_coord (mr.xval, mr.yval);
400 /* part 2: find out where the model point is now
403 mr.xpos = (*cp.model())->when;
404 mr.ypos = (*cp.model())->value;
406 /* part 3: get the position of the visual control
407 points before and after us.
410 ControlPoint* before;
413 if (cp.view_index()) {
414 before = nth (cp.view_index() - 1);
419 after = nth (cp.view_index() + 1);
422 mr.xmin = (*before->model())->when;
423 mr.ymin = (*before->model())->value;
424 mr.start = before->model();
429 mr.start = cp.model();
433 mr.end = after->model();
443 AutomationLine::determine_visible_control_points (ALPoints& points)
445 uint32_t view_index, pi, n;
446 AutomationList::iterator model;
448 double last_control_point_x = 0.0;
449 double last_control_point_y = 0.0;
450 uint32_t this_rx = 0;
451 uint32_t prev_rx = 0;
452 uint32_t this_ry = 0;
453 uint32_t prev_ry = 0;
458 /* hide all existing points, and the line */
462 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
469 if (points.empty()) {
473 npoints = points.size();
475 /* compute derivative/slope for the entire line */
477 slope = new double[npoints];
479 for (n = 0; n < npoints - 1; ++n) {
480 double xdelta = points[n+1].x - points[n].x;
481 double ydelta = points[n+1].y - points[n].y;
482 slope[n] = ydelta/xdelta;
485 box_size = (uint32_t) control_point_box_size ();
487 /* read all points and decide which ones to show as control points */
491 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
493 double tx = points[pi].x;
494 double ty = points[pi].y;
496 if (isnan (tx) || isnan (ty)) {
497 warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
502 /* now ensure that the control_points vector reflects the current curve
503 state, but don't plot control points too close together. also, don't
504 plot a series of points all with the same value.
506 always plot the first and last points, of course.
509 if (invalid_point (points, pi)) {
510 /* for some reason, we are supposed to ignore this point,
511 but still keep track of the model index.
516 if (pi > 0 && pi < npoints - 1) {
517 if (slope[pi] == slope[pi-1]) {
519 /* no reason to display this point */
525 /* need to round here. the ultimate coordinates are integer
526 pixels, so tiny deltas in the coords will be eliminated
527 and we end up with "colinear" line segments. since the
528 line rendering code in libart doesn't like this very
529 much, we eliminate them here. don't do this for the first and last
533 this_rx = (uint32_t) rint (tx);
534 this_ry = (uint32_t) rint (ty);
536 if (view_index && pi != npoints && /* not the first, not the last */
537 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
538 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
539 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
543 /* ok, we should display this point */
545 if (view_index >= cpsize) {
547 /* make sure we have enough control points */
549 ControlPoint* ncp = new ControlPoint (*this);
551 ncp->set_size (box_size);
553 control_points.push_back (ncp);
557 ControlPoint::ShapeType shape;
559 if (!terminal_points_can_slide) {
561 control_points[view_index]->set_can_slide(false);
563 shape = ControlPoint::Start;
565 shape = ControlPoint::Full;
567 } else if (pi == npoints - 1) {
568 control_points[view_index]->set_can_slide(false);
569 shape = ControlPoint::End;
571 control_points[view_index]->set_can_slide(true);
572 shape = ControlPoint::Full;
575 control_points[view_index]->set_can_slide(true);
576 shape = ControlPoint::Full;
579 last_control_point_x = tx;
580 last_control_point_y = ty;
582 control_points[view_index]->reset (tx, ty, model, view_index, shape);
587 /* finally, control visibility */
589 if (_visible && points_visible) {
590 control_points[view_index]->show ();
591 control_points[view_index]->set_visible (true);
593 if (!points_visible) {
594 control_points[view_index]->set_visible (false);
601 /* discard extra CP's to avoid confusing ourselves */
603 while (control_points.size() > view_index) {
604 ControlPoint* cp = control_points.back();
605 control_points.pop_back ();
609 if (!terminal_points_can_slide) {
610 control_points.back()->set_can_slide(false);
615 if (view_index > 1) {
617 npoints = view_index;
619 /* reset the line coordinates */
621 while (line_points.size() < npoints) {
622 line_points.push_back (Art::Point (0,0));
625 while (line_points.size() > npoints) {
626 line_points.pop_back ();
629 for (view_index = 0; view_index < npoints; ++view_index) {
630 line_points[view_index].set_x (control_points[view_index]->get_x());
631 line_points[view_index].set_y (control_points[view_index]->get_y());
634 line->property_points() = line_points;
636 if (_visible && _interpolation != AutomationList::Discrete) {
642 set_selected_points (trackview.editor().get_selection().points);
647 AutomationLine::get_verbose_cursor_string (double fraction) const
649 std::string s = fraction_to_string (fraction);
650 if (_uses_gain_mapping) {
658 * @param fraction y fraction
659 * @return string representation of this value, using dB if appropriate.
662 AutomationLine::fraction_to_string (double fraction) const
666 if (_uses_gain_mapping) {
667 if (fraction == 0.0) {
668 snprintf (buf, sizeof (buf), "-inf");
670 snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain (fraction)));
674 view_to_model_coord (dummy, fraction);
675 if (EventTypeMap::instance().is_integer (alist->parameter())) {
676 snprintf (buf, sizeof (buf), "%d", (int)fraction);
678 snprintf (buf, sizeof (buf), "%.2f", fraction);
687 * @param s Value string in the form as returned by fraction_to_string.
688 * @return Corresponding y fraction.
691 AutomationLine::string_to_fraction (string const & s) const
698 sscanf (s.c_str(), "%lf", &v);
700 if (_uses_gain_mapping) {
701 v = gain_to_slider_position (dB_to_coefficient (v));
704 model_to_view_coord (dummy, v);
711 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
713 return p[index].x == max_frames && p[index].y == DBL_MAX;
717 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
719 p[index].x = max_frames;
720 p[index].y = DBL_MAX;
724 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
726 if (trackview.editor().session() == 0) { /* how? */
733 str = _("automation event move");
735 str = _("automation range drag");
738 trackview.editor().session()->begin_reversible_command (str);
739 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
743 first_drag_fraction = fraction;
744 last_drag_fraction = fraction;
750 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
753 drag_distance += (x - drag_x);
755 drag_distance -= (drag_x - x);
760 modify_view_point (cp, x, fraction, with_push);
762 if (line_points.size() > 1) {
763 line->property_points() = line_points;
767 did_push = with_push;
771 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
773 double ydelta = fraction - last_drag_fraction;
775 did_push = with_push;
777 last_drag_fraction = fraction;
782 //check if one of the control points on the line is in a selected range
783 bool range_found = false;
786 for (uint32_t i = i1 ; i <= i2; i++) {
788 if (cp->selected()) {
794 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
795 if ((*i)->selected()) {
796 modify_view_point (*(*i), trackview.editor().unit_to_frame ((*i)->get_x()), ((_height - (*i)->get_y()) /_height) + ydelta, with_push);
801 for (uint32_t i = i1 ; i <= i2; i++) {
803 modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
807 if (line_points.size() > 1) {
808 line->property_points() = line_points;
815 AutomationLine::end_drag (ControlPoint* cp)
824 sync_model_with_view_point (*cp, did_push, drag_distance);
826 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
831 update_pending = false;
833 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
834 trackview.editor().session()->commit_reversible_command ();
835 trackview.editor().session()->set_dirty ();
840 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
842 ModelRepresentation mr;
845 model_representation (cp, mr);
847 /* how much are we changing the central point by */
849 ydelta = mr.yval - mr.ypos;
852 apply the full change to the central point, and interpolate
853 on both axes to cover all model points represented
854 by the control point.
857 /* change all points before the primary point */
859 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
861 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
862 double y_delta = ydelta * fract;
863 double x_delta = distance * fract;
867 if (y_delta || x_delta) {
868 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
872 /* change the primary point */
874 update_pending = true;
875 alist->modify (cp.model(), mr.xval, mr.yval);
878 /* change later points */
880 AutomationList::iterator i = cp.model();
884 while (i != mr.end) {
886 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
888 /* all later points move by the same distance along the x-axis as the main point */
891 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
899 /* move all points after the range represented by the view by the same distance
900 as the main point moved.
903 alist->slide (mr.end, drag_distance);
909 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
911 ControlPoint *bcp = 0;
912 ControlPoint *acp = 0;
915 unit_xval = trackview.editor().frame_to_unit (xval);
917 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
919 if ((*i)->get_x() <= unit_xval) {
921 if (!bcp || (*i)->get_x() > bcp->get_x()) {
923 before = bcp->view_index();
926 } else if ((*i)->get_x() > unit_xval) {
928 after = acp->view_index();
937 AutomationLine::is_last_point (ControlPoint& cp)
939 ModelRepresentation mr;
941 model_representation (cp, mr);
943 // If the list is not empty, and the point is the last point in the list
945 if (!alist->empty() && mr.end == alist->end()) {
953 AutomationLine::is_first_point (ControlPoint& cp)
955 ModelRepresentation mr;
957 model_representation (cp, mr);
959 // If the list is not empty, and the point is the first point in the list
961 if (!alist->empty() && mr.start == alist->begin()) {
968 // This is copied into AudioRegionGainLine
970 AutomationLine::remove_point (ControlPoint& cp)
972 ModelRepresentation mr;
974 model_representation (cp, mr);
976 trackview.editor().session()->begin_reversible_command (_("remove control point"));
977 XMLNode &before = alist->get_state();
979 alist->erase (mr.start, mr.end);
981 trackview.editor().session()->add_command(new MementoCommand<AutomationList>(
982 *alist.get(), &before, &alist->get_state()));
983 trackview.editor().session()->commit_reversible_command ();
984 trackview.editor().session()->set_dirty ();
988 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
989 double botfrac, double topfrac, list<Selectable*>& results)
996 bool collecting = false;
998 /* Curse X11 and its inverted coordinate system! */
1000 bot = (1.0 - topfrac) * _height;
1001 top = (1.0 - botfrac) * _height;
1003 nstart = max_frames;
1006 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1007 double when = (*(*i)->model())->when;
1009 if (when >= start && when <= end) {
1011 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1014 (*i)->set_visible(true);
1016 nstart = min (nstart, when);
1017 nend = max (nend, when);
1023 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, &trackview));
1025 nstart = max_frames;
1033 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, &trackview));
1039 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& /*results*/)
1045 AutomationLine::set_selected_points (PointSelection& points)
1050 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1051 (*i)->set_selected(false);
1054 if (points.empty()) {
1058 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1060 if ((*r).track != &trackview) {
1064 /* Curse X11 and its inverted coordinate system! */
1066 bot = (1.0 - (*r).high_fract) * _height;
1067 top = (1.0 - (*r).low_fract) * _height;
1069 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1071 double rstart, rend;
1073 rstart = trackview.editor().frame_to_unit ((*r).start);
1074 rend = trackview.editor().frame_to_unit ((*r).end);
1076 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1078 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1080 (*i)->set_selected(true);
1088 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1089 (*i)->show_color (false, !points_visible);
1094 void AutomationLine::set_colors() {
1095 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1096 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1097 (*i)->show_color (false, !points_visible);
1102 AutomationLine::show_selection ()
1104 TimeSelection& time (trackview.editor().get_selection().time);
1106 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1108 (*i)->set_selected(false);
1110 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1111 double rstart, rend;
1113 rstart = trackview.editor().frame_to_unit ((*r).start);
1114 rend = trackview.editor().frame_to_unit ((*r).end);
1116 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1117 (*i)->set_selected(true);
1122 (*i)->show_color (false, !points_visible);
1127 AutomationLine::hide_selection ()
1129 // show_selection ();
1133 AutomationLine::list_changed ()
1139 AutomationLine::reset_callback (const Evoral::ControlList& events)
1141 ALPoints tmp_points;
1142 uint32_t npoints = events.size();
1145 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1148 control_points.clear ();
1153 AutomationList::const_iterator ai;
1155 for (ai = events.begin(); ai != events.end(); ++ai) {
1157 double translated_x = (*ai)->when;
1158 double translated_y = (*ai)->value;
1159 model_to_view_coord (translated_x, translated_y);
1161 add_model_point (tmp_points, (*ai)->when, translated_y);
1164 determine_visible_control_points (tmp_points);
1169 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1171 tmp_points.push_back (ALPoint (trackview.editor().frame_to_unit (_time_converter.to(frame)),
1172 _height - (yfract * _height)));
1176 AutomationLine::reset ()
1178 update_pending = false;
1184 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1188 AutomationLine::clear ()
1190 /* parent must create command */
1191 XMLNode &before = get_state();
1193 trackview.editor().session()->add_command (
1194 new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1195 trackview.editor().session()->commit_reversible_command ();
1196 trackview.editor().session()->set_dirty ();
1200 AutomationLine::change_model (AutomationList::iterator /*i*/, double /*x*/, double /*y*/)
1205 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1212 AutomationLine::show_all_control_points ()
1214 points_visible = true;
1216 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1217 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1219 (*i)->set_visible (true);
1224 AutomationLine::hide_all_but_selected_control_points ()
1226 if (alist->interpolation() == AutomationList::Discrete) {
1230 points_visible = false;
1232 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1233 if (!(*i)->selected()) {
1234 (*i)->set_visible (false);
1240 AutomationLine::track_entered()
1242 if (alist->interpolation() != AutomationList::Discrete) {
1243 show_all_control_points();
1248 AutomationLine::track_exited()
1250 if (alist->interpolation() != AutomationList::Discrete) {
1251 hide_all_but_selected_control_points();
1256 AutomationLine::get_state (void)
1258 /* function as a proxy for the model */
1259 return alist->get_state();
1263 AutomationLine::set_state (const XMLNode &node, int version)
1265 /* function as a proxy for the model */
1266 return alist->set_state (node, version);
1270 AutomationLine::view_to_model_coord (double& x, double& y) const
1272 /* TODO: This should be more generic ... */
1273 if (alist->parameter().type() == GainAutomation ||
1274 alist->parameter().type() == EnvelopeAutomation) {
1275 y = slider_position_to_gain (y);
1278 } else if (alist->parameter().type() == PanAutomation) {
1279 // vertical coordinate axis reversal
1281 } else if (alist->parameter().type() == PluginAutomation) {
1282 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1284 y = (int)(y * alist->parameter().max());
1287 x = _time_converter.from(x);
1291 AutomationLine::model_to_view_coord (double& x, double& y) const
1293 /* TODO: This should be more generic ... */
1294 if (alist->parameter().type() == GainAutomation ||
1295 alist->parameter().type() == EnvelopeAutomation) {
1296 y = gain_to_slider_position (y);
1297 } else if (alist->parameter().type() == PanAutomation) {
1298 // vertical coordinate axis reversal
1300 } else if (alist->parameter().type() == PluginAutomation) {
1301 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1303 y = y / (double)alist->parameter().max(); /* ... like this */
1306 x = _time_converter.to(x);
1311 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1313 _interpolation = style;
1315 if (style == AutomationList::Discrete) {
1316 show_all_control_points();
1319 hide_all_but_selected_control_points();