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 AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> al)
63 _parent_group (parent)
65 _interpolation = al->interpolation();
66 points_visible = false;
67 update_pending = false;
68 _uses_gain_mapping = false;
71 terminal_points_can_slide = true;
74 group = new ArdourCanvas::Group (parent);
75 group->property_x() = 0.0;
76 group->property_y() = 0.0;
78 line = new ArdourCanvas::Line (*group);
79 line->property_width_pixels() = (guint)1;
80 line->set_data ("line", this);
82 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
84 alist->StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
86 trackview.session().register_with_memento_command_factory(alist->id(), this);
88 if (alist->parameter().type() == GainAutomation ||
89 alist->parameter().type() == EnvelopeAutomation) {
90 set_uses_gain_mapping (true);
93 set_interpolation(alist->interpolation());
96 AutomationLine::~AutomationLine ()
98 vector_delete (&control_points);
103 AutomationLine::event_handler (GdkEvent* event)
105 return PublicEditor::instance().canvas_line_event (event, line, this);
109 AutomationLine::queue_reset ()
111 if (!update_pending) {
112 update_pending = true;
113 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
118 AutomationLine::show ()
120 if (_interpolation != AutomationList::Discrete) {
124 if (points_visible) {
125 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
134 AutomationLine::hide ()
137 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
144 AutomationLine::control_point_box_size ()
146 if (_interpolation == AutomationList::Discrete) {
147 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
151 if (_height > TimeAxisView::hLarger) {
153 } else if (_height > (guint32) TimeAxisView::hNormal) {
160 AutomationLine::set_height (guint32 h)
165 double bsz = control_point_box_size();
167 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
168 (*i)->set_size (bsz);
176 AutomationLine::set_line_color (uint32_t color)
179 line->property_fill_color_rgba() = color;
183 AutomationLine::set_uses_gain_mapping (bool yn)
185 if (yn != _uses_gain_mapping) {
186 _uses_gain_mapping = yn;
192 AutomationLine::nth (uint32_t n)
194 if (n < control_points.size()) {
195 return control_points[n];
202 AutomationLine::modify_point_y (ControlPoint& cp, double y)
204 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
205 and needs to be converted to a canvas unit distance.
210 y = _height - (y * _height);
212 double const x = trackview.editor().frame_to_unit ((*cp.model())->when);
214 trackview.editor().current_session()->begin_reversible_command (_("automation event move"));
215 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
217 cp.move_to (x, y, ControlPoint::Full);
218 reset_line_coords (cp);
220 if (line_points.size() > 1) {
221 line->property_points() = line_points;
225 sync_model_with_view_point (cp, false, 0);
228 update_pending = false;
230 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
231 trackview.editor().current_session()->commit_reversible_command ();
232 trackview.editor().current_session()->set_dirty ();
237 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
240 uint32_t last_movable = UINT_MAX;
241 double x_limit = DBL_MAX;
243 /* this just changes the current view. it does not alter
244 the model in any way at all.
247 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
248 and needs to be converted to a canvas unit distance.
253 y = _height - (y * _height);
255 if (cp.can_slide()) {
257 /* x-coord cannot move beyond adjacent points or the start/end, and is
258 already in frames. it needs to be converted to canvas units.
261 x = trackview.editor().frame_to_unit (x);
263 /* clamp x position using view coordinates */
265 ControlPoint *before;
268 if (cp.view_index()) {
269 before = nth (cp.view_index() - 1);
270 x = max (x, before->get_x()+1.0);
277 if (cp.view_index() < control_points.size() - 1) {
279 after = nth (cp.view_index() + 1);
281 /*if it is a "spike" leave the x alone */
283 if (after->get_x() - before->get_x() < 2) {
287 x = min (x, after->get_x()-1.0);
297 /* find the first point that can't move */
299 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
300 if (!after->can_slide()) {
301 x_limit = after->get_x() - 1.0;
302 last_movable = after->view_index();
307 delta = x - cp.get_x();
312 /* leave the x-coordinate alone */
314 x = trackview.editor().frame_to_unit ((*cp.model())->when);
320 cp.move_to (x, y, ControlPoint::Full);
321 reset_line_coords (cp);
325 uint32_t limit = min (control_points.size(), (size_t)last_movable);
327 /* move the current point to wherever the user told it to go, subject
331 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
332 reset_line_coords (cp);
334 /* now move all subsequent control points, to reflect the motion.
337 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
338 ControlPoint *p = nth (i);
341 if (p->can_slide()) {
342 new_x = min (p->get_x() + delta, x_limit);
343 p->move_to (new_x, p->get_y(), ControlPoint::Full);
344 reset_line_coords (*p);
351 AutomationLine::reset_line_coords (ControlPoint& cp)
353 if (cp.view_index() < line_points.size()) {
354 line_points[cp.view_index()].set_x (cp.get_x());
355 line_points[cp.view_index()].set_y (cp.get_y());
360 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
365 update_pending = true;
367 for (uint32_t i = start; i <= end; ++i) {
369 sync_model_with_view_point (*p, false, 0);
374 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
376 /* part one: find out where the visual control point is.
377 initial results are in canvas units. ask the
378 line to convert them to something relevant.
381 mr.xval = (nframes_t) floor (cp.get_x());
382 mr.yval = 1.0 - (cp.get_y() / _height);
384 /* if xval has not changed, set it directly from the model to avoid rounding errors */
386 if (mr.xval == trackview.editor().frame_to_unit((*cp.model())->when)) {
387 mr.xval = (nframes_t) (*cp.model())->when;
389 mr.xval = trackview.editor().unit_to_frame (mr.xval);
392 /* virtual call: this will do the right thing
393 for whatever particular type of line we are.
396 view_to_model_y (mr.yval);
398 /* part 2: find out where the model point is now
401 mr.xpos = (nframes_t) (*cp.model())->when;
402 mr.ypos = (*cp.model())->value;
404 /* part 3: get the position of the visual control
405 points before and after us.
408 ControlPoint* before;
411 if (cp.view_index()) {
412 before = nth (cp.view_index() - 1);
417 after = nth (cp.view_index() + 1);
420 mr.xmin = (nframes_t) (*before->model())->when;
421 mr.ymin = (*before->model())->value;
422 mr.start = before->model();
427 mr.start = cp.model();
431 mr.end = after->model();
441 AutomationLine::determine_visible_control_points (ALPoints& points)
443 uint32_t view_index, pi, n;
444 AutomationList::iterator model;
446 double last_control_point_x = 0.0;
447 double last_control_point_y = 0.0;
448 uint32_t this_rx = 0;
449 uint32_t prev_rx = 0;
450 uint32_t this_ry = 0;
451 uint32_t prev_ry = 0;
456 /* hide all existing points, and the line */
460 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
467 if (points.empty()) {
471 npoints = points.size();
473 /* compute derivative/slope for the entire line */
475 slope = new double[npoints];
477 for (n = 0; n < npoints - 1; ++n) {
478 double xdelta = points[n+1].x - points[n].x;
479 double ydelta = points[n+1].y - points[n].y;
480 slope[n] = ydelta/xdelta;
483 box_size = (uint32_t) control_point_box_size ();
485 /* read all points and decide which ones to show as control points */
489 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
491 double tx = points[pi].x;
492 double ty = points[pi].y;
494 if (isnan (tx) || isnan (ty)) {
495 warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
500 /* now ensure that the control_points vector reflects the current curve
501 state, but don't plot control points too close together. also, don't
502 plot a series of points all with the same value.
504 always plot the first and last points, of course.
507 if (invalid_point (points, pi)) {
508 /* for some reason, we are supposed to ignore this point,
509 but still keep track of the model index.
514 if (pi > 0 && pi < npoints - 1) {
515 if (slope[pi] == slope[pi-1]) {
517 /* no reason to display this point */
523 /* need to round here. the ultimate coordinates are integer
524 pixels, so tiny deltas in the coords will be eliminated
525 and we end up with "colinear" line segments. since the
526 line rendering code in libart doesn't like this very
527 much, we eliminate them here. don't do this for the first and last
531 this_rx = (uint32_t) rint (tx);
532 this_ry = (uint32_t) rint (ty);
534 if (view_index && pi != npoints && /* not the first, not the last */
535 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
536 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
537 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
541 /* ok, we should display this point */
543 if (view_index >= cpsize) {
545 /* make sure we have enough control points */
547 ControlPoint* ncp = new ControlPoint (*this);
549 ncp->set_size (box_size);
551 control_points.push_back (ncp);
555 ControlPoint::ShapeType shape;
557 if (!terminal_points_can_slide) {
559 control_points[view_index]->set_can_slide(false);
561 shape = ControlPoint::Start;
563 shape = ControlPoint::Full;
565 } else if (pi == npoints - 1) {
566 control_points[view_index]->set_can_slide(false);
567 shape = ControlPoint::End;
569 control_points[view_index]->set_can_slide(true);
570 shape = ControlPoint::Full;
573 control_points[view_index]->set_can_slide(true);
574 shape = ControlPoint::Full;
577 last_control_point_x = tx;
578 last_control_point_y = ty;
580 control_points[view_index]->reset (tx, ty, model, view_index, shape);
585 /* finally, control visibility */
587 if (_visible && points_visible) {
588 control_points[view_index]->show ();
589 control_points[view_index]->set_visible (true);
591 if (!points_visible) {
592 control_points[view_index]->set_visible (false);
599 /* discard extra CP's to avoid confusing ourselves */
601 while (control_points.size() > view_index) {
602 ControlPoint* cp = control_points.back();
603 control_points.pop_back ();
607 if (!terminal_points_can_slide) {
608 control_points.back()->set_can_slide(false);
613 if (view_index > 1) {
615 npoints = view_index;
617 /* reset the line coordinates */
619 while (line_points.size() < npoints) {
620 line_points.push_back (Art::Point (0,0));
623 while (line_points.size() > npoints) {
624 line_points.pop_back ();
627 for (view_index = 0; view_index < npoints; ++view_index) {
628 line_points[view_index].set_x (control_points[view_index]->get_x());
629 line_points[view_index].set_y (control_points[view_index]->get_y());
632 line->property_points() = line_points;
634 if (_visible && _interpolation != AutomationList::Discrete) {
640 set_selected_points (trackview.editor().get_selection().points);
645 AutomationLine::get_verbose_cursor_string (double fraction) const
647 std::string s = fraction_to_string (fraction);
648 if (_uses_gain_mapping) {
656 * @param fraction y fraction
657 * @return string representation of this value, using dB if appropriate.
661 AutomationLine::fraction_to_string (double fraction) const
665 if (_uses_gain_mapping) {
666 if (fraction == 0.0) {
667 snprintf (buf, sizeof (buf), "-inf");
669 snprintf (buf, sizeof (buf), "%.1f", coefficient_to_dB (slider_position_to_gain (fraction)));
672 view_to_model_y (fraction);
673 if (EventTypeMap::instance().is_integer (alist->parameter())) {
674 snprintf (buf, sizeof (buf), "%d", (int)fraction);
676 snprintf (buf, sizeof (buf), "%.2f", fraction);
685 * @param s Value string in the form as returned by fraction_to_string.
686 * @return Corresponding y fraction.
690 AutomationLine::string_to_fraction (string const & s) const
697 sscanf (s.c_str(), "%lf", &v);
699 if (_uses_gain_mapping) {
700 v = gain_to_slider_position (dB_to_coefficient (v));
709 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
711 return p[index].x == max_frames && p[index].y == DBL_MAX;
715 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
717 p[index].x = max_frames;
718 p[index].y = DBL_MAX;
722 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
724 if (trackview.editor().current_session() == 0) { /* how? */
731 str = _("automation event move");
733 str = _("automation range drag");
736 trackview.editor().current_session()->begin_reversible_command (str);
737 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
741 first_drag_fraction = fraction;
742 last_drag_fraction = fraction;
748 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
751 drag_distance += (x - drag_x);
753 drag_distance -= (drag_x - x);
758 modify_view_point (cp, x, fraction, with_push);
760 if (line_points.size() > 1) {
761 line->property_points() = line_points;
765 did_push = with_push;
769 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
771 double ydelta = fraction - last_drag_fraction;
773 did_push = with_push;
775 last_drag_fraction = fraction;
782 for (uint32_t i = i1 ; i <= i2; i++) {
784 modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
787 if (line_points.size() > 1) {
788 line->property_points() = line_points;
795 AutomationLine::end_drag (ControlPoint* cp)
804 sync_model_with_view_point (*cp, did_push, drag_distance);
806 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
811 update_pending = false;
813 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
814 trackview.editor().current_session()->commit_reversible_command ();
815 trackview.editor().current_session()->set_dirty ();
820 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
822 ModelRepresentation mr;
825 model_representation (cp, mr);
827 /* how much are we changing the central point by */
829 ydelta = mr.yval - mr.ypos;
832 apply the full change to the central point, and interpolate
833 on both axes to cover all model points represented
834 by the control point.
837 /* change all points before the primary point */
839 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
841 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
842 double y_delta = ydelta * fract;
843 double x_delta = distance * fract;
847 if (y_delta || x_delta) {
848 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
852 /* change the primary point */
854 update_pending = true;
855 alist->modify (cp.model(), mr.xval, mr.yval);
858 /* change later points */
860 AutomationList::iterator i = cp.model();
864 while (i != mr.end) {
866 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
868 /* all later points move by the same distance along the x-axis as the main point */
871 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
879 /* move all points after the range represented by the view by the same distance
880 as the main point moved.
883 alist->slide (mr.end, drag_distance);
889 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
891 ControlPoint *bcp = 0;
892 ControlPoint *acp = 0;
895 /* xval is in frames */
897 unit_xval = trackview.editor().frame_to_unit (xval);
899 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
901 if ((*i)->get_x() <= unit_xval) {
903 if (!bcp || (*i)->get_x() > bcp->get_x()) {
905 before = bcp->view_index();
908 } else if ((*i)->get_x() > unit_xval) {
910 after = acp->view_index();
919 AutomationLine::is_last_point (ControlPoint& cp)
921 ModelRepresentation mr;
923 model_representation (cp, mr);
925 // If the list is not empty, and the point is the last point in the list
927 if (!alist->empty() && mr.end == alist->end()) {
935 AutomationLine::is_first_point (ControlPoint& cp)
937 ModelRepresentation mr;
939 model_representation (cp, mr);
941 // If the list is not empty, and the point is the first point in the list
943 if (!alist->empty() && mr.start == alist->begin()) {
950 // This is copied into AudioRegionGainLine
952 AutomationLine::remove_point (ControlPoint& cp)
954 ModelRepresentation mr;
956 model_representation (cp, mr);
958 trackview.editor().current_session()->begin_reversible_command (_("remove control point"));
959 XMLNode &before = alist->get_state();
961 alist->erase (mr.start, mr.end);
963 trackview.editor().current_session()->add_command(new MementoCommand<AutomationList>(
964 *alist.get(), &before, &alist->get_state()));
965 trackview.editor().current_session()->commit_reversible_command ();
966 trackview.editor().current_session()->set_dirty ();
970 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
971 double botfrac, double topfrac, list<Selectable*>& results)
978 bool collecting = false;
980 /* Curse X11 and its inverted coordinate system! */
982 bot = (1.0 - topfrac) * _height;
983 top = (1.0 - botfrac) * _height;
988 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
990 nframes_t when = (nframes_t) (*(*i)->model())->when;
992 if (when >= start && when <= end) {
994 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
997 (*i)->set_visible(true);
999 nstart = min (nstart, when);
1000 nend = max (nend, when);
1006 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1008 nstart = max_frames;
1016 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1022 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1028 AutomationLine::set_selected_points (PointSelection& points)
1033 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1034 (*i)->set_selected(false);
1037 if (points.empty()) {
1041 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1043 if (&(*r).track != &trackview) {
1047 /* Curse X11 and its inverted coordinate system! */
1049 bot = (1.0 - (*r).high_fract) * _height;
1050 top = (1.0 - (*r).low_fract) * _height;
1052 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1054 double rstart, rend;
1056 rstart = trackview.editor().frame_to_unit ((*r).start);
1057 rend = trackview.editor().frame_to_unit ((*r).end);
1059 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1061 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1063 (*i)->set_selected(true);
1071 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1072 (*i)->show_color (false, !points_visible);
1077 void AutomationLine::set_colors() {
1078 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1079 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1080 (*i)->show_color (false, !points_visible);
1085 AutomationLine::show_selection ()
1087 TimeSelection& time (trackview.editor().get_selection().time);
1089 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1091 (*i)->set_selected(false);
1093 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1094 double rstart, rend;
1096 rstart = trackview.editor().frame_to_unit ((*r).start);
1097 rend = trackview.editor().frame_to_unit ((*r).end);
1099 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1100 (*i)->set_selected(true);
1105 (*i)->show_color (false, !points_visible);
1110 AutomationLine::hide_selection ()
1112 // show_selection ();
1116 AutomationLine::list_changed ()
1122 AutomationLine::reset_callback (const Evoral::ControlList& events)
1124 ALPoints tmp_points;
1125 uint32_t npoints = events.size();
1128 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1131 control_points.clear ();
1136 AutomationList::const_iterator ai;
1138 for (ai = events.begin(); ai != events.end(); ++ai) {
1140 double translated_y = (*ai)->value;
1141 model_to_view_y (translated_y);
1143 add_model_point (tmp_points, (*ai)->when, translated_y);
1146 determine_visible_control_points (tmp_points);
1151 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1153 tmp_points.push_back (ALPoint (trackview.editor().frame_to_unit (frame),
1154 _height - (yfract * _height)));
1158 AutomationLine::reset ()
1160 update_pending = false;
1166 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1170 AutomationLine::clear ()
1172 /* parent must create command */
1173 XMLNode &before = get_state();
1175 trackview.editor().current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1176 trackview.editor().current_session()->commit_reversible_command ();
1177 trackview.editor().current_session()->set_dirty ();
1181 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1186 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1193 AutomationLine::show_all_control_points ()
1195 points_visible = true;
1197 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1198 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1200 (*i)->set_visible (true);
1205 AutomationLine::hide_all_but_selected_control_points ()
1207 if (alist->interpolation() == AutomationList::Discrete) {
1211 points_visible = false;
1213 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1214 if (!(*i)->selected()) {
1215 (*i)->set_visible (false);
1221 AutomationLine::track_entered()
1223 if (alist->interpolation() != AutomationList::Discrete) {
1224 show_all_control_points();
1229 AutomationLine::track_exited()
1231 if (alist->interpolation() != AutomationList::Discrete) {
1232 hide_all_but_selected_control_points();
1237 AutomationLine::get_state (void)
1239 /* function as a proxy for the model */
1240 return alist->get_state();
1244 AutomationLine::set_state (const XMLNode &node)
1246 /* function as a proxy for the model */
1247 return alist->set_state (node);
1251 AutomationLine::view_to_model_y (double& y) const
1253 /* TODO: This should be more generic ... */
1254 if (alist->parameter().type() == GainAutomation ||
1255 alist->parameter().type() == EnvelopeAutomation) {
1256 y = slider_position_to_gain (y);
1259 } else if (alist->parameter().type() == PanAutomation) {
1260 // vertical coordinate axis reversal
1262 } else if (alist->parameter().type() == PluginAutomation) {
1263 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1265 y = (int)(y * alist->parameter().max());
1270 AutomationLine::model_to_view_y (double& y) const
1272 /* TODO: This should be more generic ... */
1273 if (alist->parameter().type() == GainAutomation ||
1274 alist->parameter().type() == EnvelopeAutomation) {
1275 y = gain_to_slider_position (y);
1276 } else if (alist->parameter().type() == PanAutomation) {
1277 // vertical coordinate axis reversal
1279 } else if (alist->parameter().type() == PluginAutomation) {
1280 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1282 y = y / (double)alist->parameter().max(); /* ... like this */
1288 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1290 _interpolation = style;
1292 if (style == AutomationList::Discrete) {
1293 show_all_control_points();
1296 hide_all_but_selected_control_points();